home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Cream of the Crop 1
/
Cream of the Crop 1.iso
/
PROGRAM
/
ANIVGA.ARJ
/
ANIVGA.PAS
< prev
next >
Wrap
Pascal/Delphi Source File
|
1991-11-09
|
209KB
|
6,347 lines
{$A+,B-,D+,E-,O-,R-,S-,V-}
{$M 16384,0,655360}
{Programmtool zur Realisierung schneller Animationen auf der VGA-Grafik- }
{karte von: Kai Rohrbacher, 1988-1991, Turbo-Pascal 6.0 }
{ Features: }
{ - flickerfreie Animation durch page-flipping, Auswertung des vertikalen }
{ Retracesignals und Verwendung eines speziellen VGA-256-Farbengrafikmodus}
{ - Spritebewegung pixelweise (und nicht nur byteweise) moeglich }
{ - beliebiges Hintergrundbild, vor der die Animation geschieht }
{ - volle Unterstuetzung der 256-Farbmoeglichkeiten der VGA-Karte }
{ - verschiedene Spritedarstellungsmoeglichkeiten: }
{ - Sprites koennen pixelweise als durchsichtig gegenueber dem Hintergrund}
{ deklariert werden }
{ - Sprites koennen ihre Farbe in Abhaengigkeit ihres momentanen Hinter- }
{ grundes veraendern ("Schattenfunktion") }
{ - Routine zur exakten Feststellung der Kollision zweier Sprites }
{ - Sprites werden beim Verschwinden an einer der Bildschirmgrenzen korrekt }
{ abgeschnitten }
{ - Verwaltung von bis zu 1000 verschiedenen Sprites }
{ - gleichzeitige Darstellung von bis zu 500 Sprites }
{ - maximale Spritegroesse 64k }
{ - maximaler Umfang aller Sprites zusammen nur durch Hauptspeicher begrenzt}
{ - arbeitet mit virtuellen Koordinaten im Bereich -16000..+16000, daher }
{ einfaches horizontales und vertikales Scrolling moeglich }
{ - Scrollbares Hintergrundbild ebenfalls unterstuetzt }
{ - viele unterstuetzende Routinen: zeichnen von Linien (mit eingebautem }
{ Clipping-Algorithmus), Punkten und Grafik-Text (dto.), automatische Ver-}
{ waltungs des Heaps zum Speichern/Laden von Sprites, Hintergrundbildern, }
{ Aendern von Spritedarstellungsmodi waehrend der Laufzeit, Geschwindig- }
{ keitsanpassung an unterschiedlich schnelle Rechner, ... }
UNIT ANIVGA;
INTERFACE
USES CRT,DOS;
CONST NMAX=499;
XMAX=319;
YMAX=199;
LoadMAX=1000; {max. Anzahl an gleichzeitig geladenenen Sprites}
LINESIZE=(XMAX+1) DIV 4; {Groesse einer Zeile=80 Bytes}
PAGESIZE=(YMAX+1)*LINESIZE; {200 Zeilen zu je 320/4 Bytes}
BACKGNDPAGE=2;
SCROLLPAGE=3;
STATIC=0; {Konstanten fuer Hintergrundart}
SCROLLING=1;
MaxTiles=10000; {max. Anzahl an Hintergrund-Kacheln}
StartVirtualX:INTEGER=0; {obere linke Bildschirmecke}
StartVirtualY:INTEGER=0;
{unterstuetzte Darstellungsmodi der Sprites: }
Display_NORMAL=0; {normal : durchsichtig fuer Farbe 0}
Display_FAST =1; {schnell : keine Hintergrundberuecksichtigung}
Display_SHADOW=2; {Schatten: Farbumsetzung anhand des Hintergrundes}
Display_UNKNOWN=255;{Fehlerwert}
{Fehlercodes des Animationspaketes: }
Err_None=0;
Err_NotEnoughMemory=1;
Err_FileIO=2;
Err_InvalidSpriteNumber=3;
Err_NoSprite=4;
Err_InvalidPageNumber=5;
Err_NoVGA=6;
Err_NoPicture=7;
Err_InvalidPercentage=8;
Err_NoTile=9;
Err_InvalidTileNumber=10;
Err_InvalidCoordinates=11;
Err_BackgroundToBig=12;
Err_InvalidMode=13;
Err_InvalidSpriteLoadNumber=3;
TYPE FontChar=ARRAY[0..5] OF BYTE;
Font=ARRAY[0..255] OF Fontchar;
FontOrient=(horizontal,vertical); {moegliche Textausgaberichtungen}
CONST GraphTextOrientation:FontOrient=horizontal; {aktuelle Ausgaberichtung}
GraphTextColor:BYTE=white; {aktuelle Textausgabefarben}
GraphTextBackground:BYTE=white;
FontHeight=6;
FontWidth=6;
TYPE Table=ARRAY[0..NMAX] OF INTEGER;
ColorTable=ARRAY[0..255] OF BYTE;
VAR Error:BYTE; {globale Fehlervariable}
SpriteN:Table;
SpriteX:Table;
SpriteY:Table;
NextSprite:ARRAY[0..LoadMAX] OF WORD;
SPRITEAD:ARRAY[0..LoadMAX] OF WORD;
PAGE,PAGEADR,SCROLLADR,BACKGNDADR:WORD;
Color:BYTE; {Zeichenfarbe fuer Linien}
was_cut:BOOLEAN; {TRUE/FALSE, falls "GetImage" clippen musste }
left_cut, {Var., die durch "GetImage" gesetzt werden und}
right_cut, {bei "was_cut"=TRUE darueber Auskunft geben, }
top_cut, {wo und wieviel des Bildes abgeschnitten wer- }
bottom_cut:WORD; {den musste }
BackgroundMode:BYTE;
BackTile:ARRAY[0..MaxTiles-1] OF BYTE; {Kachelnspeicher}
XTiles,YTiles:INTEGER; {Breite,Hoehe des def. Bereiches}
BackX1,BackY1,BackX2,BackY2:INTEGER; {Koordinaten des def. Bereiches }
PROCEDURE ShadowTab;
PROCEDURE SetShadowTab(brightness:BYTE);
PROCEDURE SetCycleTime(milliseconds:WORD);
PROCEDURE SetSpriteCycle(nr,len:WORD);
FUNCTION GetImage(x1,y1,x2,y2:INTEGER; pa:BYTE):POINTER;
PROCEDURE PutImage(x,y:INTEGER; p:POINTER; pa:BYTE);
PROCEDURE InitGraph;
PROCEDURE Screen(pa:BYTE);
PROCEDURE Line(x1,y1,x2,y2:INTEGER; pa:BYTE);
PROCEDURE BackgroundLine(x1,y1,x2,y2:INTEGER);
FUNCTION GetPixel(x,y:INTEGER):BYTE;
FUNCTION BackgroundGetPixel(x,y:INTEGER):BYTE;
FUNCTION PageGetPixel(x,y:INTEGER; pa:BYTE):BYTE;
PROCEDURE PutPixel(x,y:INTEGER; color:Byte);
PROCEDURE BackgroundPutPixel(x,y:INTEGER; color:Byte);
PROCEDURE PagePutPixel(x,y:INTEGER; color,pa:Byte);
PROCEDURE OutTextXY(x,y:INTEGER; pa:BYTE; s:STRING);
PROCEDURE BackgroundOutTextXY(x,y:INTEGER; s:STRING);
FUNCTION Hitdetect(s1,s2:INTEGER):BOOLEAN;
PROCEDURE Animate;
FUNCTION LoadSprite(name:String; number:WORD):WORD;
FUNCTION LoadTile(name:STRING; number:BYTE):WORD;
PROCEDURE SetBackgroundScrollRange(x1,y1,x2,y2:INTEGER);
PROCEDURE SetBackgroundMode(mode:BYTE);
PROCEDURE PutTile(x,y:INTEGER; TileNr:BYTE);
PROCEDURE SetModeByte(Sp:WORD; M:BYTE);
FUNCTION GetModeByte(Sp:WORD):BYTE;
PROCEDURE FillPage(pa,color:Byte);
PROCEDURE FillBackground(color:BYTE);
PROCEDURE GetBackgroundFromPage(pa:Byte);
PROCEDURE WritePage(name:STRING; pa:BYTE);
PROCEDURE LoadPage(name:STRING; pa:BYTE);
PROCEDURE WriteBackgroundPage(name:STRING);
PROCEDURE LoadBackgroundPage(name:STRING);
PROCEDURE InitRoutines;
PROCEDURE CloseRoutines;
FUNCTION GetErrorMessage:STRING;
{--------------------------------------------------------------------------}
IMPLEMENTATION
CONST StartIndex=0;
EndIndex=StartIndex+3;
{Offsetadressen der Grafikseiten (in Segment $A000):}
Offset_Adr:Array[StartIndex..EndIndex] OF WORD=($0000,$3E80,$7D00,$BB80);
{Segmentadressen der Grafikseiten (bei Offset = 0) :}
Segment_Adr:ARRAY[StartIndex..EndIndex] OF WORD=($A000,$A3E8,$A7D0,$ABB8);
{Sprite(header)aufbau:
0..1 DW Plane_0_Daten
2..3 DW Plane_1_Daten
4..5 DW Plane_2_Daten
6..7 DW Plane_3_Daten
8..9 DW Breite (in 4er-Gruppen)
10..11 DW Hoehe in Zeilen
12..15 DB 1,2,4,8 ; Translate-Tabelle fuer Port-Ansteuerung
16..17 DW SpriteLength ; Laenge der Spritedatei
; jetzt fuer temporaere Variablen reservierter
; Bereich:
18..19 DW ? ; licutoff_ | hit1xfirst
20..21 DW ? ; zeilenadr | hit1yfirst
22..23 DW ? ; bildx | hit2xfirst
24..25 DW ? ; yoffset_ | hit2yfirst
26..27 DW ? ; end_min_start | ueberlappx_1
28..29 DW ? ; | ueberlappy_1
30..31 DW ? ; | x1
32..33 DW ? ; | x2
34..35 DW ? ; | y1
36..37 DW ? ; | y2
38..39 DB 'K','R' ; Kennung als Sprite
40 DB 1 ; Versionsnummer
41 DB 0 ; Modusnummer fuer Sprite
42..43 DW linke_Begrenzungen
44..45 DW rechte_Begrenzungen
46..47 DW obere_Begrenzungen
48..49 DW untere_Begrenzungen
50..?? DB Daten
zum Bsp.: xxrxxxxx, mit: r=rot=40, g=gruen=45, b=blau=35, x=weiss=30
xrgrxxxx
rbgbrxxx
}
{Adressen von wichtigen Werten innerhalb des Spriteheaders:}
Left=42;
Right=44;
Top=46;
Bottom=48;
Breite=8;
Hoehe=10;
Translate=12;
SpriteLength=16;
Kennung=38;
Version=40;
Modus=41;
{Adressen der temporaeren Variablen fuer die Zeichenroutinen:}
licutoff_=18;
zeilenadr=20;
bildx=22;
yoffset_=24;
end_min_start=26;
{Adressen der temporaeren Variablen fuer die Zeichenroutinen:}
hit1xfirst=18;
hit1yfirst=20;
hit2xfirst=22;
hit2yfirst=24;
ueberlappx_1=26;
ueberlappy_1=28;
x1=30;
x2=32;
y1=34;
y2=36;
TranslateTab:ARRAY[0..3] OF BYTE=(1,2,4,8); {Fuer Maskenadressierung}
PICHeader:STRING[3]='PIC'; {Kennung in Bilderdateien}
TYPE SpriteHeader= RECORD
Zeiger_auf_Plane:Array[0..3] OF Word;
Breite_in_4er_Gruppen:WORD;
Hoehe_in_Zeilen:WORD;
Translate:Array[1..4] OF Byte;
SpriteLength:WORD;
Dummy:Array[1..10] OF Word;
Kennung:ARRAY[1..2] OF CHAR;
Version:BYTE;
Modus:BYTE;
ZeigerL,ZeigerR,ZeigerO,ZeigerU:Word;
END;
CONST FontMask:ARRAY[0..7] OF BYTE=($80,$40,$20,$10,8,4,2,1);
FontData: Font=( {verwendeter Zeichensatz: }
( 0, 0, 0, 0, 0, 0), {#0} {selbstgestrickter 6x6 Font}
( 0,216, 0,248,112, 0), {#1}
(248,168,248,136,216,248), {#2}
( 0, 80,248,248,112, 32), {#3}
( 0, 32,112,248,112, 32), {#4}
( 32,112,216,248, 32,112), {#5}
( 32,112,248,248, 32,112), {#6}
( 0, 32,216,216, 32, 0), {#7}
(248,216,168,168,216,248), {#8}
( 0,112,200,152,112, 0), {#9}
(248,136,168,168,136,248), {#10}
( 56, 24, 32,112,136,112), {#11}
(112,136,112, 32,248, 32), {#12}
( 56, 40, 32, 32,224,224), {#13}
( 0,120, 72,120, 72,216), {#14}
( 0, 32,168, 80,168, 32), {#15}
( 0,128,224,248,224,128), {#16}
( 0, 8, 56,248, 56, 8), {#17}
( 32,112,168,168,112, 32), {#18}
( 0,216,216,216, 0,216), {#19}
( 0,120,168,104, 40, 40), {#20}
( 24, 32, 16, 40,144, 96), {#21}
( 0, 0, 0, 0,248,248), {#22}
( 32,112, 32,112, 32,248), {#23}
( 0, 32,112,248, 32, 32), {#24}
( 0, 32, 32,248,112, 32), {#25}
( 0, 32, 16,248, 16, 32), {#26}
( 0, 32, 64,248, 64, 32), {#27}
( 0, 0,192,248, 0, 0), {#28}
( 0, 0, 80,248, 80, 0), {#29}
( 0, 0, 0, 32,112,248), {#30}
( 0, 0,248,112, 32, 0), {#31}
( 0, 0, 0, 0, 0, 0), { }
( 0, 48, 48, 48, 0, 48), {!}
( 0, 80, 80, 0, 0, 0), {"}
( 0, 80,248, 80,248, 80), {#}
( 32,120,160,112, 40,240), { $}
( 0,200, 16, 32, 64,152), {%}
( 0,112,216,112,152,104), {&}
( 0, 16, 32, 0, 0, 0), {'}
( 0,112,192,192,192,112), {(}
( 0,224, 48, 48, 48,224), {)}
( 0, 80, 32,248, 32, 80), {*}
( 0, 0, 32,248, 32, 0), {+}
( 0, 0, 0, 32, 32, 64), {,}
( 0, 0, 0,248, 0, 0), {-}
( 0, 0, 0, 0, 48, 0), {.}
( 4, 8, 16, 32, 64,128), {/}
( 0,112,152,168,200,112), {0}
( 0, 48,112, 48, 48,120), {1}
( 0,240, 24,112,192,248), {2}
( 0,240, 24,112, 24,240), {3}
( 0,192,208,248, 48, 48), {4}
( 0,248,192,240, 24,240), {5}
( 0,248,128,248,136,248), {6}
( 0,248, 24, 48, 96, 96), {7}
( 0,112,216,112,216,112), {8}
( 0,112,136,120, 8,112), {9}
( 0, 0, 32, 0, 32, 0), {:}
( 0, 0, 32, 0, 32, 64), {;}
( 0, 24, 48, 96, 48, 24), {<}
( 0, 0,248, 0,248, 0), {=}
( 0, 96, 48, 24, 48, 96), {>}
(112,136, 16, 32, 0, 32), {?}
( 0,112,136,184,128,120), {@}
( 0,112,200,248,200,200), {A}
( 0,240,200,240,200,240), {B}
( 0,120,192,192,192,120), {C}
( 0,240,216,200,216,240), {D}
( 0,248,192,240,192,248), {E}
( 0,248,192,240,192,192), {F}
( 0,120,192,216,200,120), {G}
( 0,200,200,248,200,200), {H}
( 0,120, 48, 48, 48,120), {I}
( 0,248, 8, 8,200,112), {J}
( 0,200,208,224,208,200), {K}
( 0,192,192,192,192,248), {L}
( 0,136,216,168,136,136), {M}
( 0,136,200,168,152,136), {N}
( 0,112,200,200,200,112), {O}
( 0,240,200,240,192,192), {P}
( 0, 96,208,208,208,104), {Q}
( 0,240,136,240,208,200), {R}
( 0,248,192,248, 24,248), {S}
( 0,248, 96, 96, 96, 96), {T}
( 0,200,200,200,200,248), {U}
( 0,200,200,200,200, 48), {V}
( 0,136,136,168,248, 80), {W}
( 0,136,216,112,216,136), {X}
( 0,200,200,112, 48, 48), {Y}
( 0,248, 24,112,192,248), {Z}
( 0,120, 96, 96, 96,120), {[}
(128, 64, 32, 16, 8, 4), {\}
( 0,120, 24, 24, 24,120), {]}
( 32, 80,136, 0, 0, 0), {^}
( 0, 0, 0, 0, 0,248), {_}
( 64, 32, 0, 0, 0, 0), {`}
( 0, 0,112,200,200,120), {a}
( 0,128,240,136,136,240), {b}
( 0, 0,120,192,192,120), {c}
( 0, 8,120,136,136,120), {d}
( 0, 0,112,248,128,112), {e}
( 0, 24, 32,120, 32, 32), {f}
( 0,112,136,120, 8,112), {g}
( 0,192,240,200,200,200), {h}
( 48, 0, 48, 48, 48, 48), {i}
( 24, 0, 24, 24,216,112), {j}
( 0,192,208,224,216,216), {k}
( 0, 96, 96, 96, 96, 56), {l}
( 0, 0,208,248,168,136), {m}
( 0, 0,240,200,200,200), {n}
( 0, 0,112,200,200,112), {o}
( 0, 0,240,200,240,192), {p}
( 0, 0,112,152,120, 24), {q}
( 0, 0,176,104, 96, 96), {r}
( 0, 56, 64, 48,136,112), {s}
( 0, 96,248, 96,104, 48), {t}
( 0, 0,200,200,200,120), {u}
( 0, 0,200,200,200,112), {v}
( 0, 0,136,168,168,112), {w}
( 0, 0,216, 96, 48,216), {x}
( 0, 0,200,248, 8,112), {y}
( 0, 0,240, 48,192,248), {z}
( 0, 56, 96,192, 96, 56),(*{*)
( 0, 16, 16, 0, 16, 16), {|}
( 0,224, 48, 24, 48,224),(*}*)
( 0,104,144, 0, 0, 0), {~}
( 0, 32, 80,136,248, 0), {#127}
(112,200,128,200,112,192), {#128}
( 0,200, 0,200,200,120), {#129}
( 24, 32,112,248,128,112), {#130}
( 16, 40, 0,120,196,124), {#131}
(104, 0,112,200,200,120), {#132}
( 48, 8,112,136,136,120), {#133}
( 16, 40, 16,112,200,120), {#134}
( 0,120,192,120, 16, 96), {#135}
(112, 0,112,248,192,112), {#136}
( 80, 0,112,248,128,112), {#137}
( 48, 8,112,248,192,112), {#138}
(104, 0, 48, 48, 48, 48), {#139}
( 48, 72, 0, 48, 48, 48), {#140}
( 96, 16, 0, 48, 48, 48), {#141}
(200, 0,112,200,248,200), {#142}
( 48, 0,112,200,248,200), {#143}
(112,248,192,240,192,248), {#144}
( 0,208, 40,112,160, 88), {#145}
( 0, 56, 80,248,144,152), {#146}
( 32, 80, 0,112,200,112), {#147}
( 80, 0,112,200,200,112), {#148}
( 96, 16, 0,112,200,112), {#149}
( 32, 80, 0,200,200,120), {#150}
( 96, 16, 0,200,200,120), {#151}
( 80, 0,200,248, 8,112), {#152}
( 80, 0,112,200,200,112), {#153}
(200, 0,200,200,200,248), {#154}
( 16,120,128,128,120, 16), {#155}
( 48, 72,224, 64,136,248), {#156}
(216, 32,248, 32,248, 32), {#157}
(192,160,208,184,144,152), {#158}
( 48, 40, 96, 48,160, 96), {#159}
( 48, 64, 0,112,136,120), {#160}
( 48, 64, 0, 32, 32, 32), {#161}
( 48, 64, 0,112,200,112), {#162}
( 48, 64, 0,200,200,120), {#163}
(104,144, 0,176, 72, 72), {#164}
(104,144, 0,200,168,152), {#165}
(112,144,104, 0,248, 0), {#166}
(112,136,112, 0,248, 0), {#167}
( 32, 0, 32, 64,136,112), {#168}
( 0, 0,252,192, 0, 0), {#169}
( 0, 0,252, 12, 0, 0), {#170}
( 72, 80, 32, 64,168, 40), {#171}
( 72, 80, 32, 80,152, 8), {#172}
( 48, 0, 48, 48, 48, 0), {#173}
( 40, 80,160, 80, 40, 0), {#174}
(160, 80, 40, 80,160, 0), {#175}
( 84,168, 84,168, 84,168), {#176}
(252,252,252,252,252,252), {#177}
(168, 84,168, 84,168, 84), {#178}
( 16, 16, 16, 16, 16, 16), {#179}
( 16, 16, 16,240, 16, 16), {#180}
( 16, 16,240, 16,240, 16), {#181}
( 40, 40, 40,232, 40, 40), {#182}
( 0, 0, 0,248, 40, 40), {#183}
( 0, 0,240, 16,240, 16), {#184}
( 40, 40,232, 8,232, 40), {#185}
( 40, 40, 40, 40, 40, 40), {#186}
( 0, 0,248, 8,232, 40), {#187}
( 40, 40,232, 8,248, 0), {#188}
( 40, 40, 40,248, 0, 0), {#189}
( 16, 16,240, 16,240, 0), {#190}
( 0, 0, 0,240, 16, 16), {#191}
( 16, 16, 16, 28, 0, 0), {#192}
( 16, 16, 16,252, 0, 0), {#193}
( 0, 0, 0,252, 16, 16), {#194}
( 16, 16, 16, 28, 16, 16), {#195}
( 0, 0, 0,252, 0, 0), {#196}
( 16, 16, 16,252, 16, 16), {#197}
( 16, 16, 28, 16, 28, 16), {#198}
( 40, 40, 40, 44, 40, 40), {#199}
( 40, 40, 44, 32, 60, 0), {#200}
( 0, 0, 60, 32, 44, 40), {#201}
( 40, 40,236, 0,252, 0), {#202}
( 0, 0,252, 0,236, 40), {#203}
( 40, 40, 44, 32, 44, 40), {#204}
( 0, 0,252, 0,252, 0), {#205}
( 40, 40,236, 0,236, 40), {#206}
( 16, 16,252, 0,252, 0), {#207}
( 40, 40, 40,252, 0, 0), {#208}
( 0, 0,252, 0,252, 16), {#209}
( 0, 0, 0,252, 40, 40), {#210}
( 40, 40, 40, 60, 0, 0), {#211}
( 16, 16, 28, 16, 28, 0), {#212}
( 0, 0, 28, 16, 28, 16), {#213}
( 0, 0, 0, 60, 40, 40), {#214}
( 40, 40, 40,252, 40, 40), {#215}
( 16, 16,252, 16,252, 16), {#216}
( 16, 16, 16,240, 0, 0), {#217}
( 0, 0, 28, 16, 16, 16), {#218}
(252,252,252,252,252,252), {#219}
( 0, 0, 0,252,252,252), {#220}
(192,192,192,192,192,192), {#221}
( 12, 12, 12, 12, 12, 12), {#222}
(252,252,252, 0, 0, 0), {#223}
( 0, 0,104,144,144,104), {#224}
( 0,112,152,176,136,176), {#225}
( 0,248,136,128,128,128), {#226}
( 0, 0,248, 80, 80, 80), {#227}
(248, 72, 32, 64,136,248), {#228}
( 0, 0,120,144,144, 96), {#229}
( 0, 72, 72,120, 64,192), {#230}
( 0, 0,104,176, 32, 32), {#231}
( 0,248, 32, 80, 32,248), {#232}
( 0,112,136,248,136,112), {#233}
( 0,112,136,136, 80,216), {#234}
( 56, 64, 32,112,136,112), {#235}
( 0, 0, 80,168, 80, 0), {#236}
( 0, 8, 80,168, 80,128), {#237}
( 0,120,128,248,128,120), {#238}
( 0, 0,112,136,136,136), {#239}
( 0,248, 0,248, 0,248), {#240}
( 0, 32,112, 32, 0,248), {#241}
( 64, 32, 16, 32, 64,248), {#242}
( 16, 32, 64, 32, 16,248), {#243}
( 16, 40, 32, 32, 32, 32), {#244}
( 32, 32, 32, 32,160, 64), {#245}
( 0, 32, 0,248, 0, 32), {#246}
(104,144, 0,104,144, 0), {#247}
( 96,144, 96, 0, 0, 0), {#248}
( 0, 0, 0, 48, 0, 0), {#249}
( 0, 0, 0, 16, 0, 0), {#250}
( 60, 32, 32,160, 96, 32), {#251}
(176, 72, 72, 0, 0, 0), {#252}
(224, 16, 96,128,240, 0), {#253}
( 0, 0,112,112, 0, 0), {#254}
( 0, 0, 0, 0, 0, 0));{#255}
VAR Steigung:BYTE; {entscheidet, welcher Alg. Anwendung findet}
DY_mal2,DY_m_DX_mal2:INTEGER;
oldMode:byte;
regs:registers;
IsAT:BYTE;
TimeFlag:BYTE;
CycleTime:LONGINT;
{-----------------------------------------------------}
PROCEDURE ShadowTab; ASSEMBLER;
{Pseudoprozedur, um Daten der Farbumsetztabelle im Codesegment unterzu-}
{bringen, AUF KEINEN FALL AUFRUFEN!!! }
{Defaultwerte entsprechen Abdunkelung auf 70% des Farb-Helligkeitswerts}
ASM
DB 254,104,120,124,112,108,114, 24, 20,128,144, 3,136, 5,140, 7
DB 254,254, 17, 17, 18, 19, 20, 20, 21, 8, 23, 24, 24, 25, 26, 7
DB 1, 1,107,108, 5,108,109, 4, 4, 4, 6, 6,116,116,117, 2
DB 2, 2,123,124, 3,124,125, 1,152,155,156,156, 5,156,156,157
DB 160,163,164,164,164,164,164,165,168,171,172,172, 3,172,172,173
DB 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24
DB 24, 24, 24, 24, 24, 24, 24, 24,176,177,178,179,180,181,182,183
DB 184,185,186,187,188,189,190,191,192,193,194,195,196,197,198,199
DB 200,201,203,204,204,204,205,207,208,209,211,212,212,212,213,215
DB 216,217,219,220,220,220,221,223,246,227,228,228,228,228,228,229
DB 234,235,236,236,236,236,236,237,242,243,244,244,244,244,244,245
DB 254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254
DB 254,254,254,254,254,254,254,254, 17, 17, 17, 17, 17, 17, 17, 17
DB 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17
DB 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17
DB 17, 17, 17, 17, 17, 17, 17, 17,254,254,254,254,254,254,254, 7
END;
PROCEDURE CS_TranslateTab; ASSEMBLER;
{kleine Pseudoprozedur, um die Umsetztabelle fuer die Bitmaske auch im}
{Codesegment zu haben}
ASM
DB 1,2,4,8
END;
PROCEDURE SetShadowTab(brightness:BYTE);
{ in: brightness = gewuenschte Helligkeit der Farben im Schattenbereich,}
{ in Prozent zu der Helligkeit ihrer Originalfarben }
{out: ShadowTab = (Naeherungs-)Farbtabelle fuer gewuenschte Abdunkelung}
{rem: Defaultwert in ShadowTab ist 70% Helligkeit der Ursprungsfarben! }
{ Diese Routine dauert ihre Zeit (ca. 4 sec auf 8MHz-AT!) }
TYPE PaletteEntry=RECORD red,green,blue:BYTE END;
Palette=ARRAY[0..255] OF PaletteEntry;
CONST default_Farben:Palette= {Defaultfarben-Palette des 256-Farbmodus}
( {ausgelesen mithilfe des BIOS-Aufrufs: }
(red: 0; green: 0; blue: 0), { MOV AX,1017h ;lese Palettenregister}
(red: 0; green: 0; blue: 42), { XOR BX,BX ;von Farbe 0 an }
(red: 0; green: 42; blue: 0), { MOV CX,100h ;alle 256 Farben}
(red: 0; green: 42; blue: 42), { LES DX,Ziel ;nach ES:DX }
(red: 42; green: 0; blue: 0), { INT 10h }
(red: 42; green: 0; blue: 42), {Achtung! Die Werte koenn(t)en nur dann }
(red: 42; green: 21; blue: 0), {ausgelesen werden, wenn der Grafikmodus}
(red: 42; green: 42; blue: 42), {bereits aktiv ist, deshalb wurden sie }
(red: 21; green: 21; blue: 21), {hier "statisch" aufgenommen!}
(red: 21; green: 21; blue: 63),
(red: 21; green: 63; blue: 21),
(red: 21; green: 63; blue: 63),
(red: 63; green: 21; blue: 21),
(red: 63; green: 21; blue: 63),
(red: 63; green: 63; blue: 21),
(red: 63; green: 63; blue: 63),
(red: 0; green: 0; blue: 0),
(red: 5; green: 5; blue: 5),
(red: 8; green: 8; blue: 8),
(red: 11; green: 11; blue: 11),
(red: 14; green: 14; blue: 14),
(red: 17; green: 17; blue: 17),
(red: 20; green: 20; blue: 20),
(red: 24; green: 24; blue: 24),
(red: 28; green: 28; blue: 28),
(red: 32; green: 32; blue: 32),
(red: 36; green: 36; blue: 36),
(red: 40; green: 40; blue: 40),
(red: 45; green: 45; blue: 45),
(red: 50; green: 50; blue: 50),
(red: 56; green: 56; blue: 56),
(red: 63; green: 63; blue: 63),
(red: 0; green: 0; blue: 63),
(red: 16; green: 0; blue: 63),
(red: 31; green: 0; blue: 63),
(red: 47; green: 0; blue: 63),
(red: 63; green: 0; blue: 63),
(red: 63; green: 0; blue: 47),
(red: 63; green: 0; blue: 31),
(red: 63; green: 0; blue: 16),
(red: 63; green: 0; blue: 0),
(red: 63; green: 16; blue: 0),
(red: 63; green: 31; blue: 0),
(red: 63; green: 47; blue: 0),
(red: 63; green: 63; blue: 0),
(red: 47; green: 63; blue: 0),
(red: 31; green: 63; blue: 0),
(red: 16; green: 63; blue: 0),
(red: 0; green: 63; blue: 0),
(red: 0; green: 63; blue: 16),
(red: 0; green: 63; blue: 31),
(red: 0; green: 63; blue: 47),
(red: 0; green: 63; blue: 63),
(red: 0; green: 47; blue: 63),
(red: 0; green: 31; blue: 63),
(red: 0; green: 16; blue: 63),
(red: 31; green: 31; blue: 63),
(red: 39; green: 31; blue: 63),
(red: 47; green: 31; blue: 63),
(red: 55; green: 31; blue: 63),
(red: 63; green: 31; blue: 63),
(red: 63; green: 31; blue: 55),
(red: 63; green: 31; blue: 47),
(red: 63; green: 31; blue: 39),
(red: 63; green: 31; blue: 31),
(red: 63; green: 39; blue: 31),
(red: 63; green: 47; blue: 31),
(red: 63; green: 55; blue: 31),
(red: 63; green: 63; blue: 31),
(red: 55; green: 63; blue: 31),
(red: 47; green: 63; blue: 31),
(red: 39; green: 63; blue: 31),
(red: 31; green: 63; blue: 31),
(red: 31; green: 63; blue: 39),
(red: 31; green: 63; blue: 47),
(red: 31; green: 63; blue: 55),
(red: 31; green: 63; blue: 63),
(red: 31; green: 55; blue: 63),
(red: 31; green: 47; blue: 63),
(red: 31; green: 39; blue: 63),
(red: 45; green: 45; blue: 63),
(red: 49; green: 45; blue: 63),
(red: 54; green: 45; blue: 63),
(red: 58; green: 45; blue: 63),
(red: 63; green: 45; blue: 63),
(red: 63; green: 45; blue: 58),
(red: 63; green: 45; blue: 54),
(red: 63; green: 45; blue: 49),
(red: 63; green: 45; blue: 45),
(red: 63; green: 49; blue: 45),
(red: 63; green: 54; blue: 45),
(red: 63; green: 58; blue: 45),
(red: 63; green: 63; blue: 45),
(red: 58; green: 63; blue: 45),
(red: 54; green: 63; blue: 45),
(red: 49; green: 63; blue: 45),
(red: 45; green: 63; blue: 45),
(red: 45; green: 63; blue: 49),
(red: 45; green: 63; blue: 54),
(red: 45; green: 63; blue: 58),
(red: 45; green: 63; blue: 63),
(red: 45; green: 58; blue: 63),
(red: 45; green: 54; blue: 63),
(red: 45; green: 49; blue: 63),
(red: 0; green: 0; blue: 28),
(red: 7; green: 0; blue: 28),
(red: 14; green: 0; blue: 28),
(red: 21; green: 0; blue: 28),
(red: 28; green: 0; blue: 28),
(red: 28; green: 0; blue: 21),
(red: 28; green: 0; blue: 14),
(red: 28; green: 0; blue: 7),
(red: 28; green: 0; blue: 0),
(red: 28; green: 7; blue: 0),
(red: 28; green: 14; blue: 0),
(red: 28; green: 21; blue: 0),
(red: 28; green: 28; blue: 0),
(red: 21; green: 28; blue: 0),
(red: 14; green: 28; blue: 0),
(red: 7; green: 28; blue: 0),
(red: 0; green: 28; blue: 0),
(red: 0; green: 28; blue: 7),
(red: 0; green: 28; blue: 14),
(red: 0; green: 28; blue: 21),
(red: 0; green: 28; blue: 28),
(red: 0; green: 21; blue: 28),
(red: 0; green: 14; blue: 28),
(red: 0; green: 7; blue: 28),
(red: 14; green: 14; blue: 28),
(red: 17; green: 14; blue: 28),
(red: 21; green: 14; blue: 28),
(red: 24; green: 14; blue: 28),
(red: 28; green: 14; blue: 28),
(red: 28; green: 14; blue: 24),
(red: 28; green: 14; blue: 21),
(red: 28; green: 14; blue: 17),
(red: 28; green: 14; blue: 14),
(red: 28; green: 17; blue: 14),
(red: 28; green: 21; blue: 14),
(red: 28; green: 24; blue: 14),
(red: 28; green: 28; blue: 14),
(red: 24; green: 28; blue: 14),
(red: 21; green: 28; blue: 14),
(red: 17; green: 28; blue: 14),
(red: 14; green: 28; blue: 14),
(red: 14; green: 28; blue: 17),
(red: 14; green: 28; blue: 21),
(red: 14; green: 28; blue: 24),
(red: 14; green: 28; blue: 28),
(red: 14; green: 24; blue: 28),
(red: 14; green: 21; blue: 28),
(red: 14; green: 17; blue: 28),
(red: 20; green: 20; blue: 28),
(red: 22; green: 20; blue: 28),
(red: 24; green: 20; blue: 28),
(red: 26; green: 20; blue: 28),
(red: 28; green: 20; blue: 28),
(red: 28; green: 20; blue: 26),
(red: 28; green: 20; blue: 24),
(red: 28; green: 20; blue: 22),
(red: 28; green: 20; blue: 20),
(red: 28; green: 22; blue: 20),
(red: 28; green: 24; blue: 20),
(red: 28; green: 26; blue: 20),
(red: 28; green: 28; blue: 20),
(red: 26; green: 28; blue: 20),
(red: 24; green: 28; blue: 20),
(red: 22; green: 28; blue: 20),
(red: 20; green: 28; blue: 20),
(red: 20; green: 28; blue: 22),
(red: 20; green: 28; blue: 24),
(red: 20; green: 28; blue: 26),
(red: 20; green: 28; blue: 28),
(red: 20; green: 26; blue: 28),
(red: 20; green: 24; blue: 28),
(red: 20; green: 22; blue: 28),
(red: 0; green: 0; blue: 16),
(red: 4; green: 0; blue: 16),
(red: 8; green: 0; blue: 16),
(red: 12; green: 0; blue: 16),
(red: 16; green: 0; blue: 16),
(red: 16; green: 0; blue: 12),
(red: 16; green: 0; blue: 8),
(red: 16; green: 0; blue: 4),
(red: 16; green: 0; blue: 0),
(red: 16; green: 4; blue: 0),
(red: 16; green: 8; blue: 0),
(red: 16; green: 12; blue: 0),
(red: 16; green: 16; blue: 0),
(red: 12; green: 16; blue: 0),
(red: 8; green: 16; blue: 0),
(red: 4; green: 16; blue: 0),
(red: 0; green: 16; blue: 0),
(red: 0; green: 16; blue: 4),
(red: 0; green: 16; blue: 8),
(red: 0; green: 16; blue: 12),
(red: 0; green: 16; blue: 16),
(red: 0; green: 12; blue: 16),
(red: 0; green: 8; blue: 16),
(red: 0; green: 4; blue: 16),
(red: 8; green: 8; blue: 16),
(red: 10; green: 8; blue: 16),
(red: 12; green: 8; blue: 16),
(red: 14; green: 8; blue: 16),
(red: 16; green: 8; blue: 16),
(red: 16; green: 8; blue: 14),
(red: 16; green: 8; blue: 12),
(red: 16; green: 8; blue: 10),
(red: 16; green: 8; blue: 8),
(red: 16; green: 10; blue: 8),
(red: 16; green: 12; blue: 8),
(red: 16; green: 14; blue: 8),
(red: 16; green: 16; blue: 8),
(red: 14; green: 16; blue: 8),
(red: 12; green: 16; blue: 8),
(red: 10; green: 16; blue: 8),
(red: 8; green: 16; blue: 8),
(red: 8; green: 16; blue: 10),
(red: 8; green: 16; blue: 12),
(red: 8; green: 16; blue: 14),
(red: 8; green: 16; blue: 16),
(red: 8; green: 14; blue: 16),
(red: 8; green: 12; blue: 16),
(red: 8; green: 10; blue: 16),
(red: 11; green: 11; blue: 16),
(red: 12; green: 11; blue: 16),
(red: 13; green: 11; blue: 16),
(red: 15; green: 11; blue: 16),
(red: 16; green: 11; blue: 16),
(red: 16; green: 11; blue: 15),
(red: 16; green: 11; blue: 13),
(red: 16; green: 11; blue: 12),
(red: 16; green: 11; blue: 11),
(red: 16; green: 12; blue: 11),
(red: 16; green: 13; blue: 11),
(red: 16; green: 15; blue: 11),
(red: 16; green: 16; blue: 11),
(red: 15; green: 16; blue: 11),
(red: 13; green: 16; blue: 11),
(red: 12; green: 16; blue: 11),
(red: 11; green: 16; blue: 11),
(red: 11; green: 16; blue: 12),
(red: 11; green: 16; blue: 13),
(red: 11; green: 16; blue: 15),
(red: 11; green: 16; blue: 16),
(red: 11; green: 15; blue: 16),
(red: 11; green: 13; blue: 16),
(red: 11; green: 12; blue: 16),
(red: 0; green: 0; blue: 0),
(red: 0; green: 0; blue: 0),
(red: 0; green: 0; blue: 0),
(red: 0; green: 0; blue: 0),
(red: 0; green: 0; blue: 0),
(red: 0; green: 0; blue: 0),
(red: 0; green: 0; blue: 0),
(red: 63; green: 63; blue: 63)
);
VAR neue_Tabelle:ColorTable;
oldColor,newColor:PaletteEntry;
i,j,p:BYTE;
min:WORD;
BEGIN
IF (brightness<0) OR (brightness>100)
THEN BEGIN
Error:=Err_InvalidPercentage;
exit
END;
FOR i:=0 TO 255 DO
BEGIN
{neue Farbe=alte, abgedunkelt auf "brightness" Prozent:}
newColor.red :=default_Farben[i].red * brightness DIV 100;
newColor.green:=default_Farben[i].green* brightness DIV 100;
newColor.blue :=default_Farben[i].blue * brightness DIV 100;
{nun fuer diese Farbe eine moeglichst gute Naeherung finden:}
min:=65535;
FOR j:=255 DOWNTO 0 DO
BEGIN
oldColor:=default_Farben[j];
ASM
MOV AL,newColor.red {Farbdifferenz im Rotanteil berechnen:}
SUB AL,oldColor.red
JL @heller {<0 bedeutet: neue Farbe waere heller!}
MOV DL,AL {>=0, quadrieren (um Differenzquadrat }
MUL DL {zu minmieren) }
MOV BX,AX {Dieses Fehlerquadrat in BX speichern }
MOV AL,newColor.green {dto. fuer gruenen Farbanteil... }
SUB AL,oldColor.green
JL @heller
MOV DL,AL
MUL DL
ADD BX,AX
JC @heller {(Uebertrag bedeutet sofortiges Ende) }
MOV AL,newColor.blue {...und fuer den blauen Anteil auch }
SUB AL,oldColor.blue
JL @heller
MOV DL,AL
MUL DL
ADD AX,BX
JNC @fertig
@heller: {hierher, wenn heller: maximale Abwei-}
MOV AX,0FFFFh {ung zurueckgeben, da nur dunklere }
@fertig: {Farben gewuenscht sind! }
{Abweichungsmass steht nun in AX! }
CMP AX,min {falls Abweichung kleiner als bisheri-}
JAE @noNewMin {ges Minimum ist die Farbe j der neue }
MOV min,AX {beste Naeherungswert: ihn selbst (in }
MOV AL,j {min) und diese Farbe (in p) merken }
MOV p,AL
@noNewMin:
END;
END; {of FOR j}
neue_Tabelle[i]:=p {p = bester Naeherungswert fuer "newColor" }
END;
MOVE(neue_Tabelle,@ShadowTab^,256); {Farbtabelle uebernehmen}
END;
{Nun folgen die Codestuecke, die Verwendung finden, um ein Sprite auf den}
{Schirm zu bringen; die Schnittstelle ist fuer alle gleich: }
{ in: CX = Anzahl Bytes, die von... }
{ DS:SI = (Zeiger auf Quelladresse) nach... }
{ ES:BX = (Zeiger auf Zieladresses) zu bringen sind; }
{ DI = Bitplane (0..3) (=X-Koordinate AND 3) }
{ Die Bitmaske fuer den richtigen Schreibe-Planezugriff wurde bereits}
{ gesetzt, eine evtl. noetige Leseplane dagegen nicht! }
{ Die Routinen koennen sicher sein, dass CX<>0 ist }
{rem: Jede dieser Routinen MUSS EXAKT 16 Bytes lang und VOLL RELOKATIBEL }
{ sein, sowie die Register BP,DS,ES unveraendert lassen!!!!!!!!!!!!! }
{ Ausserdem muessen die einzelnen Routinen zur Unterscheidbarkeit in }
{ ihren ersten zwei Bytes paarweise verschieden sein! }
PROCEDURE Modus0; ASSEMBLER;
{Modus 0 betrachtet die Farbe 0 als durchsichtig fuer den Hintergrund}
ASM
INC CX
STC
SBB BX,SI
@L1:
LODSB
OR AL,AL
LOOPZ @L1
JCXZ @L2
MOV ES:[BX+SI],AL
JMP @L1 {short}
@L2:
END;
PROCEDURE Modus1; ASSEMBLER;
{Modus 1 schreibt die Daten sofort ohne weitere Untersuchung auf den Schirm}
ASM
MOV DI,BX
REP MOVSB
JMP @L3
NOP {10 Bytes Platzhalter}
NOP
NOP
NOP
NOP
NOP
NOP
NOP
NOP
NOP
@L3:
END;
PROCEDURE Modus2Work; ASSEMBLER;
{Fortsetzung von Modus2 - all das, was nicht mehr in 16 Bytes unterzubringen}
{war, kommt hierher}
ASM
OUT DX,AX {Lesezugriff auf passende Plane ermoeglichen}
PUSH DS {DS zeigt noch auf Spritedaten, muss aber auf}
{Hintergrund zeigen! }
MOV AX,ES {DS:SI := ES:DI (Quellzeiger:=Zielzeiger) }
MOV DS,AX
MOV SI,DI
MOV BX,OFFSET ShadowTab {Zeiger auf Farbumsetztabelle setzen }
@L4:
LODSB {Hintergrundfarbe holen... }
SEGCS XLAT {...mit Farbtabelle umsetzen}
STOSB {...und auf aktueller Seite darstellen}
LOOP @L4
POP DS
END;
PROCEDURE Modus2; ASSEMBLER;
{Modus 2 ist fuer "Schatten" und aehnliches gedacht: hierbei werden die }
{eigentlichen Spritedaten ignoriert und stattdessen die Hintergrunddaten}
{gelesen, die sich an der Spriteposition befinden und deren Farbwerte }
{gegen die der Farbtabelle "ShadowTab" ersetzt (um bspw. Schatten zu er-}
{zeugen, muesste diese Tabelle zu jeder Farbe eine dunklere enthalten) }
ASM
MOV AX,DI {Bitplane fuer Lesezugriff nach AX bringen}
MOV DI,BX {fuer Stringbefehle die Zieladresse nach DI bringen}
MOV AH,AL {Bitplane ins Highbyte bringen}
MOV AL,4
MOV DX,3CEh
MOV SI,OFFSET Modus2Work {fieser Trick: "CALL Modus2Work" wuerde (da re-}
CALL SI {lativ codiert) zu falscher Adresse verzweigen!}
END;
PROCEDURE Adressen; ASSEMBLER;
{Tabelle der Startadressen der 3 Routinen im Codesegment}
ASM
DW OFFSET Modus0
DW OFFSET Modus1
DW OFFSET Modus2
END;
PROCEDURE GADR; ASSEMBLER;
{Tabelle der Grafikzeilen-Startadressen (Offset-Anteil)}
ASM
DW $0000,$0050,$00A0,$00F0,$0140,$0190,$01E0,$0230
DW $0280,$02D0,$0320,$0370,$03C0,$0410,$0460,$04B0
DW $0500,$0550,$05A0,$05F0,$0640,$0690,$06E0,$0730
DW $0780,$07D0,$0820,$0870,$08C0,$0910,$0960,$09B0
DW $0A00,$0A50,$0AA0,$0AF0,$0B40,$0B90,$0BE0,$0C30
DW $0C80,$0CD0,$0D20,$0D70,$0DC0,$0E10,$0E60,$0EB0
DW $0F00,$0F50,$0FA0,$0FF0,$1040,$1090,$10E0,$1130
DW $1180,$11D0,$1220,$1270,$12C0,$1310,$1360,$13B0
DW $1400,$1450,$14A0,$14F0,$1540,$1590,$15E0,$1630
DW $1680,$16D0,$1720,$1770,$17C0,$1810,$1860,$18B0
DW $1900,$1950,$19A0,$19F0,$1A40,$1A90,$1AE0,$1B30
DW $1B80,$1BD0,$1C20,$1C70,$1CC0,$1D10,$1D60,$1DB0
DW $1E00,$1E50,$1EA0,$1EF0,$1F40,$1F90,$1FE0,$2030
DW $2080,$20D0,$2120,$2170,$21C0,$2210,$2260,$22B0
DW $2300,$2350,$23A0,$23F0,$2440,$2490,$24E0,$2530
DW $2580,$25D0,$2620,$2670,$26C0,$2710,$2760,$27B0
DW $2800,$2850,$28A0,$28F0,$2940,$2990,$29E0,$2A30
DW $2A80,$2AD0,$2B20,$2B70,$2BC0,$2C10,$2C60,$2CB0
DW $2D00,$2D50,$2DA0,$2DF0,$2E40,$2E90,$2EE0,$2F30
DW $2F80,$2FD0,$3020,$3070,$30C0,$3110,$3160,$31B0
DW $3200,$3250,$32A0,$32F0,$3340,$3390,$33E0,$3430
DW $3480,$34D0,$3520,$3570,$35C0,$3610,$3660,$36B0
DW $3700,$3750,$37A0,$37F0,$3840,$3890,$38E0,$3930
DW $3980,$39D0,$3A20,$3A70,$3AC0,$3B10,$3B60,$3BB0
DW $3C00,$3C50,$3CA0,$3CF0,$3D40,$3D90,$3DE0,$3E30
END;
FUNCTION AT:BOOLEAN;
{ in: - }
{out: TRUE/FALSE, wenn die Maschine (mindestens) ein AT ist}
BEGIN
AT:=MEM[$F000:$FFFE]=$FC
END;
PROCEDURE SetCycleTime(milliseconds:WORD);
{ in: Mindestzeit eines Animationszyklus in Millisekunden}
{out: CycleTime := dieser Wert in Mikrosekunden}
{ TimeFlag := $80}
{rem: Fuer den ersten Animationszyklus nach Aufruf dieser Routine}
{ gilt wg. TimeFlag:=$80 die Zeitbedingung noch nicht! }
{ Schaltet der Benutzer (durch Angabe von milliseconds=0) die}
{ Zeitueberwachung explizit ab, so wird das durch IsAT:=$80, }
{ d.h.: "Rechner ist ein PC" vorgetaeuscht. Sonst ist IsAT=0 }
BEGIN
TimeFlag:=$80;
CycleTime:=LONGINT(milliseconds)*LONGINT(1000);
IF (milliseconds<>0) AND AT
THEN IsAT:=0 {ja, Zeitueberwachung soll benutzt werden }
ELSE IsAT:=$80 {nein, keine moeglich oder nicht gewuenscht}
END;
PROCEDURE SetSpriteCycle(nr,len:WORD);
{ in: nr = Spriteladenummer des ersten Sprites des Zyklus }
{ len = Laenge des zu definierenden Spritezyklus }
{out: NextSprite[nr] bis NextSprite[nr+len-1] wurden so gesetzt,}
{ dass sie im Ring aufeinander zeigen, d.h.: sie bilden }
{ einen Spritezyklus}
{rem: Soll der Zyklus aus nicht direkt aufeinanderfolgenden }
{ (physikalischen) Sprites gebildet werden, so muessen die }
{ entsprechenden Eintraege in NextSprite[] manuell gemacht }
{ werden }
{ Diese Routine verwendet SpriteLADEnummern!}
VAR i:WORD;
BEGIN
IF (nr<1) OR (nr+len-1>LoadMAX)
THEN Error:=Err_InvalidSpriteLoadNumber
ELSE BEGIN
FOR i:=nr TO nr+len-2 DO NextSprite[i]:=SUCC(i);
NextSprite[PRED(nr+len)]:=nr {letztes Sprite zeigt auf erstes}
END;
END;
FUNCTION GetImage(x1,y1,x2,y2:INTEGER; pa:BYTE):POINTER;
{ in: (x1,y1) = linke obere Ecke des zu sichernden Bildausschnittes }
{ (x2,y2) = rechte untere Ecke dazu (alles virtuelle Koordinaten!) }
{ pa = Grafikseite, von der der Ausschnitt zu sichern ist (0..2) }
{ StartVirtualX,StartVirtualY = linke obere Bildschirmecke }
{out: Zeiger auf Heapbereich, der den kopierten Bildausschnitt enthaelt }
{ left_cut= evtl. noetiger linker Cutoff des Bildausschnittes (gibt an,}
{ um wieviel Punkte der Ausschnitt links ausserhalb des Bild-}
{ schirm ragte) }
{ righ_cut,top_cut,bottom_cut = dto., fuer andere Raender }
{ was_cut = TRUE/FALSE, falls ein zurechtstutzen des Bildausschnittes }
{ noetig war/nicht noetig war }
{rem: Der benoetigte Speicher wird von der Routine automatisch reserviert }
{ Sollte dies nicht moeglich sein (oder liegt der Bildausschnitt gaenz-}
{ lich ausserhalb des sichtbaren Bereichs), so wird NIL zurueckgegeben!}
{ Nur wenn "was_cut" TRUE ist, sind die Werte der globalen "..._cut" }
{ Variablen <>0 gesetzt worden, d.h.: ist der Ausschnitt _ganz_ ausser-}
{ halb des Bildes (also zurueckgegebener Zeiger=NIL), dann liefert die }
{ Routine trotzdem "was_cut"=FALSE!}
VAR len,breite,hoehe,StartAdr,actualAdr,SegmAdr:WORD;
p:POINTER;
BEGIN
was_cut:=FALSE; left_cut:=0; right_cut:=0; top_cut:=0; bottom_cut:=0;
dec(x1,StartVirtualX); {Bildschirmkoordinaten berechnen}
dec(y1,StartVirtualY);
IF (x1>XMAX) or (y1>YMAX) or (x2<0) or (y2<0) or (x1>x2) or (y1>y2)
THEN BEGIN {Bildausschnitt nicht auf dem Bildschirm}
GetImage:=NIL;
exit
END;
{Ausschnitt auf Bildschirm zurechtklippen:}
IF x1<0 THEN BEGIN left_cut :=-x1; x1:=0; was_cut:=TRUE END;
IF y1<0 THEN BEGIN top_cut:=-y1; y1:=0; was_cut:=TRUE END;
IF x2>XMAX THEN BEGIN right_cut :=x2-XMAX; x2:=XMAX; was_cut:=TRUE END;
IF y2>YMAX THEN BEGIN bottom_cut:=y2-YMAX; y2:=YMAX; was_cut:=TRUE END;
breite:=SUCC(x2-x1); hoehe:=SUCC(y2-y1);
len:=breite*hoehe+2*2; {1 Pixel=1 Byte; dazu: 2 Woerter fuer breite & hoehe}
IF len>MaxAvail
THEN BEGIN
Error:=Err_NotEnoughMemory;
GetImage:=NIL;
exit
END;
IF (pa<>0) AND (pa<>1) AND (pa<>BACKGNDPAGE) {Seitennummer muss 0..2 sein}
THEN BEGIN
Error:=Err_InvalidPageNumber;
GetImage:=NIL;
exit
END
ELSE SegmAdr:=Segment_Adr[pa];
GetMem(p,len); {Speicher auf dem Heap besorgen}
ASM
CLD
LES DI,p {ES:DI = Zeiger auf den besorgten Speicher}
MOV AX,breite
STOSW {Breite zuerst ablegen...}
MOV AX,hoehe
STOSW {...gefolgt von der Hoehe, danach dann die Daten}
MOV BX,AX {BX:=hoehe (fuer spaeter) }
MOV SI,y1
SHL SI,1
MOV SI,CS:[OFFSET gadr + SI] {SI:=y1*LINESIZE}
MOV AX,x1
MOV DL,AL
SHR AX,1
SHR AX,1
ADD SI,AX {SI:=Offsetanteil der Startadresse}
MOV StartAdr,SI
MOV actualAdr,SI
AND DL,3
MOV AH,DL
MOV AL,4
MOV DX,3CEh
OUT DX,AX {Startplane anwaehlen}
MOV DS,SegmAdr
{DS:SI = Zeiger auf erstes zu speicherndes Byte; ES:DI = Zieladr. dafuer}
{AH = Startplane, AL = 4, BX = abzuarbeitende Zeilenanzahl}
MOV DX,breite
ADD DX,3
SHR DX,1
SHR DX,1 {DX = Anzahl zu sichernde Bytes je Zeile}
@L1:
MOV CX,DX {Daten einer Zeile abspeichern}
REP MOVSB
MOV SI,actualAdr {Quellzeiger um 1 Grafikzeile weitersetzen}
ADD SI,LINESIZE
MOV actualAdr,SI
DEC BX {Zeilenzaehler verringern}
JNE @L1
INC AH {naechste Plane anwaehlen}
CMP AH,4
JNE @nowrap1 {wrap in den Bitplanes bedeutet: Startadresse}
MOV AH,0 {um 1 Adresse weitersetzen! }
INC StartAdr
@nowrap1:
MOV DX,3CEh
OUT DX,AX
MOV BX,hoehe
MOV DX,breite
INC DX
INC DX
SHR DX,1
SHR DX,1
MOV SI,StartAdr
MOV actualAdr,SI
@L2:
MOV CX,DX
REP MOVSB
MOV SI,actualAdr
ADD SI,LINESIZE
MOV actualAdr,SI
DEC BX
JNE @L2
INC AH
CMP AH,4
JNE @nowrap2
MOV AH,0
INC StartAdr
@nowrap2:
MOV DX,3CEh
OUT DX,AX
MOV BX,hoehe
MOV DX,breite
INC DX
SHR DX,1
SHR DX,1
MOV SI,StartAdr
MOV actualAdr,SI
@L3:
MOV CX,DX
REP MOVSB
MOV SI,actualAdr
ADD SI,LINESIZE
MOV actualAdr,SI
DEC BX
JNE @L3
INC AH
CMP AH,4
JNE @nowrap3
MOV AH,0
INC StartAdr
@nowrap3:
MOV DX,3CEh
OUT DX,AX
MOV BX,hoehe
MOV DX,breite
SHR DX,1
SHR DX,1
MOV SI,StartAdr
MOV actualAdr,SI
@L4:
MOV CX,DX
REP MOVSB
MOV SI,actualAdr
ADD SI,LINESIZE
MOV actualAdr,SI
DEC BX
JNE @L4
MOV AX,SEG @DATA
MOV DS,AX
END;
GetImage:=p
END;
PROCEDURE PutImage(x,y:INTEGER; p:POINTER; pa:BYTE);
{ in: (x,y) = linke obere Ecke des Zieles (in virtuellen Koordinaten) }
{ p = Zeiger auf (durch GetImage erstellten) Bildausschnitt }
{ pa = Grafikseite, in die der Bildausschnitt kopiert werden soll}
{ StartVirtualX,StartVirtualY = linke obere Bildschirmecke }
{out: - }
{rem: Der Bildausschnitt wurde zur Bildschirmdarstellung zurechtgeklippt}
{ Bei Uebergabe von NIL als Zeiger stellt die Routine gar nicht dar;}
{ Dies hilft fuer eine direkte Uebernahme der von GetImage gelie- }
{ ferten Werte! }
VAR breite,hoehe,SegmAdr,actualAdr,StartAdr,breite1,breite2,breite3,breite4,
licut_div4,topcut,pl_adr1,pl_adr2,pl_adr3,pl_adr4:WORD;
licutoff,temp:INTEGER;
BEGIN
IF p=NIL THEN exit;
dec(x,StartVirtualX); {Bildschirmkoordinaten berechnen}
dec(y,StartVirtualY);
IF (x>XMAX) or (y>YMAX) THEN exit;
IF (pa<>0) AND (pa<>1) AND (pa<>BACKGNDPAGE)
THEN BEGIN
Error:=Err_InvalidPageNumber;
exit
END
ELSE SegmAdr:=Segment_Adr[pa];
breite:=MEMW[SEG(p^):OFS(p^)];
hoehe :=MEMW[SEG(p^):OFS(p^)+2];
IF (x+breite<=0) or (y+hoehe<=0) THEN exit;
IF x<0 THEN BEGIN licutoff:=-x; x:=0 END
ELSE licutoff:=0;
IF y<0 THEN BEGIN
topcut:=-y;
y:=0
END
ELSE topcut:=0;
breite1:=(breite + 3) shr 2; {Breite einer Zeile fuer die erste, zweite,}
breite2:=(breite + 2) shr 2; {dritte und vierte Bitplane}
breite3:=(breite + 1) shr 2;
breite4:=(breite + 0) shr 2;
{Anfangsadressen der 4 Bitplanes berechnen; dabei evtl. linken cutoff mit}
{einbeziehen (plus 4 Bytes zum ueberspringen von "breite" und "hoehe" }
licut_div4:=licutoff shr 2;
pl_adr1:=4 +licut_div4 +topcut*breite1;
pl_adr2:=4 +licut_div4 +topcut*breite2 +hoehe*breite1;
pl_adr3:=4 +licut_div4 +topcut*breite3 +hoehe*(breite1+breite2);
pl_adr4:=4 +licut_div4 +topcut*breite4 +hoehe*(breite1+breite2+breite3);
{licutoff mod 4 gibt an, in welcher Reihenfolge die Punkte aus dem Heap }
{gelesen werden muessen: 0 = Planereihenfolge (1,2,3,4); 1=(2,3,4,1); }
{2=(3,4,1,2); 3=(4,1,2,3); zu beachten ist, dass die Breiten der einzel-}
{nen Bitplanetabellen natuerlich mit diesen verbunden bleibt und deshalb}
{mitgetauscht werden muss!}
ASM
CLD
MOV AX,licutoff
AND AL,3
OR AL,AL
JE @no_exchange
CMP AL,1
JNE @L10
MOV AX,pl_adr2 {Verschiebung um 1 Bit: }
MOV BX,pl_adr3 {AX=Plane2,BX=Plane3,CX=Plane4,DX=Plane1}
MOV CX,pl_adr4
MOV DX,pl_adr1 {wrap-around, deshalb: Adresse um 1 erhoehen, was }
INC DX {einer Weitersetzung um 4 Bildpunkte entspricht }
MOV pl_adr1,AX {(z.B.: Pixel (1,5,9,...),(2,6,10,...),(3,7,11,...)}
MOV pl_adr2,BX {und (0,4,8,...); letztere Bitplane wird um 1 Byte }
MOV pl_adr3,CX {weitergesetzt: liefert (richtige) (4,8,12,...) }
MOV pl_adr4,DX {Folge (Planes abwechselnd von oben nach unten lesen!}
MOV AX,breite2 {Jetzt Planebreiten: }
MOV BX,breite3 {AX=Plane2,BX=Plane3,CX=Plane4,DX=Plane1}
MOV CX,breite4
MOV DX,breite1
JMP @store
@L10:
CMP AL,2
JNE @L20
MOV AX,pl_adr3 {Verschiebung um 2 Bit: }
MOV BX,pl_adr4 {AX=Plane3,BX=Plane4,CX=Plane1,DX=Plane2}
MOV CX,pl_adr1
INC CX
MOV DX,pl_adr2
INC DX
MOV pl_adr1,AX
MOV pl_adr2,BX
MOV pl_adr3,CX
MOV pl_adr4,DX
MOV AX,breite3 {dto. fuer Planebreiten: }
MOV BX,breite4 {AX=Plane3,BX=Plane4,CX=Plane1,DX=Plane2}
MOV CX,breite1
MOV DX,breite2
JMP @store
@L20:
MOV AX,pl_adr4 {Verschiebung um 3 Bit: }
MOV BX,pl_adr1 {AX=Plane4,BX=Plane1,CX=Plane2,DX=Plane3}
INC BX
MOV CX,pl_adr2
INC CX
MOV DX,pl_adr3
INC DX
MOV pl_adr1,AX
MOV pl_adr2,BX
MOV pl_adr3,CX
MOV pl_adr4,DX
MOV AX,breite4 {dto. fuer Planebreiten: }
MOV BX,breite1 {AX=Plane4,BX=Plane1,CX=Plane2,DX=Plane3}
MOV CX,breite2
MOV DX,breite3
@store:
MOV breite1,AX
MOV breite2,BX
MOV breite3,CX
MOV breite4,DX
@no_exchange: {jetzt gilt: (pl_adr?,breite?) enthalten die Source-}
{Bitplanes/-breiten in der richtigen Reihenfolge }
MOV AX,topcut
SUB hoehe,AX {Hoehe um evtl. oberen cutoff korrigieren}
MOV AX,licutoff
SUB breite,AX {dto. fuer Breite und linken cutoff}
MOV AX,x {falls Ausschnitt ueber rechten Bildschirmrand}
ADD AX,breite {ragen wuerde: rechten cutoff bestimmen }
SUB AX,XMAX+1
JLE @no_recutoff
SUB breite,AX {AX Punkte rechts abschneiden}
@no_recutoff:
MOV AX,y {genau dasselbe fuer unteren Bildschirmrand}
ADD AX,hoehe
SUB AX,YMAX+1
JLE @no_bocutoff
SUB hoehe,AX {AX Zeilen unten abschneiden}
@no_bocutoff:
LDS SI,p
ADD pl_adr2,SI {Offsetanteil des Zeigers zu den Planeadr. addieren}
ADD pl_adr3,SI
ADD pl_adr4,SI
ADD SI,pl_adr1 {breite,hoehe und Teile oberhalb des Bildschirms}
MOV ES,SegmAdr
MOV DI,y
SHL DI,1
MOV DI,CS:[OFFSET gadr + DI] {DI:=y*LINESIZE}
MOV AX,x
MOV BL,AL
SHR AX,1
SHR AX,1
ADD DI,AX {DI:=y*LINESIZE +(x DIV 4)}
MOV StartAdr,DI
MOV actualAdr,DI
AND BX,3 {Startplane:=x mod 3}
MOV AH,CS:[OFFSET CS_TranslateTab + BX]
MOV AL,2
MOV DX,3C4h
OUT DX,AX {als Schreibplane anwaehlen}
MOV DX,hoehe
MOV DI,actualAdr
{DS:SI = Zeiger auf Daten, ES:DI = Zieladresse dafuer auf dem Schirm,}
{AH = Bitmaske fuer Zugriff, AL = 2 }
MOV BX,breite
ADD BX,3
SHR BX,1
SHR BX,1
mov cx,bx
@L1:
push si
REP MOVSB
pop si
mov cx,bx
add si,breite1
MOV DI,actualAdr
ADD DI,LINESIZE
MOV actualAdr,DI
DEC DX
JNE @L1
SHL AH,1 {naechste Bitplane anwaehlen; bei einem wrap von}
CMP AH,16 {Bitplane 3 zu Bitplane 0 muss dabei die Start- }
JNE @nowrap1 {adresse um 1 Byte weitergesetzt werden }
MOV AH,1
INC StartAdr
@nowrap1:
MOV DX,3C4h
OUT DX,AX
MOV SI,pl_adr2
MOV DI,StartAdr
MOV actualAdr,DI
MOV DX,hoehe
MOV BX,breite
INC BX
INC BX
SHR BX,1
SHR BX,1
mov cx,bx
@L2:
push si
REP MOVSB
pop si
mov cx,bx
add si,breite2
MOV DI,actualAdr
ADD DI,LINESIZE
MOV actualAdr,DI
DEC DX
JNE @L2
SHL AH,1
CMP AH,16
JNE @nowrap2
MOV AH,1
INC StartAdr
@nowrap2:
MOV DX,3C4h
OUT DX,AX
MOV SI,pl_adr3
MOV DI,StartAdr
MOV actualAdr,DI
MOV DX,hoehe
MOV BX,breite
INC BX
SHR BX,1
SHR BX,1
mov cx,bx
@L3:
push si
REP MOVSB
pop si
mov cx,bx
add si,breite3
MOV DI,actualAdr
ADD DI,LINESIZE
MOV actualAdr,DI
DEC DX
JNE @L3
SHL AH,1
CMP AH,16
JNE @nowrap3
MOV AH,1
INC StartAdr
@nowrap3:
MOV DX,3C4h
OUT DX,AX
MOV SI,pl_adr4
MOV DI,StartAdr
MOV actualAdr,DI
MOV DX,hoehe
MOV BX,breite
SHR BX,1
SHR BX,1
mov cx,bx
@L4:
push si
REP MOVSB
pop si
mov cx,bx
add si,breite4
MOV DI,actualAdr
ADD DI,LINESIZE
MOV actualAdr,DI
DEC DX
JNE @L4
MOV AX,SEG @DATA
MOV DS,AX
END;
END;
PROCEDURE Screen(pa:BYTE);
{ in: pa = anzuzeigende Bildschirmseite (0..3) }
{out: - }
{rem: Es wurde auf die Darstellung der Grafikseite pa umgeschaltet }
{ Dabei wurde NICHT auf irgendwelche Retrace-Signale synchronisiert}
{ Sinnvoll sind nur die Seiten 0 oder 1, es findet aber keine }
{ Ueberpruefung statt!}
BEGIN
ASM
MOV DX,$3D4 {CRT-Controller}
MOV AL,$0D {LB-Startadress-Register}
CLI {Darf keinenfalls unterbrochen werden!}
OUT DX,AL
INC DX
{Realisiere "AX:=Offset_Adr[pa]":}
MOV BL,pa
MOV SI,BX
AND SI,3 {Page-Wert *2 (da Worteintraege!)}
SHL SI,1 {dazu Startadresse des Feldes addieren}
ADD SI,OFFSET Offset_Adr-StartIndex*2 {evtl. Verschiebung korrigieren}
LODSW {und Wert holen}
OUT DX,AL {LB der neuen Startadresse setzen}
DEC DX
MOV AL,$0C
OUT DX,AL
INC DX
MOV AL,AH {HB der neuen Startadresse setzen}
OUT DX,AL
STI
END;
END;
PROCEDURE InitGraph;
{ in: PAGE = aktuelle Grafikseite}
{out: - }
{rem: Schaltet die VGA-Karte in den 320x200x256x4-Modus; ACHTUNG! }
{ Dieser Modus ist verschieden vom Modus $13 der VGA!!! }
{ Dabei wird auf die Darstellung der Seite 1-PAGE geschaltet }
BEGIN
INLINE(
$B8/$13/$00/ {0100: MOV AX,0013 ;Mit BIOS Grafikmodus $13 }
$CD/$10/ {0103: INT 10 ;(=320x200x256) setzen }
$BA/$C4/$03/ {0105: MOV DX,03C4 ;Sequenzer ansteuern und dort }
$B0/$04/ {0108: MOV AL,04 ;das Speichermodus-Register }
$EE/ {010A: OUT DX,AL ;auswaehlen }
$42/ {010B: INC DX ;Dessen Daten ueber das zuge- }
$EC/ {010C: IN AL,DX ;hoerige Datenregister einlesen }
$24/$F7/ {010D: AND AL,F7 ;Bit 3:=0:4 Planes nicht chainen}
$0C/$04/ {010F: OR AL,04 ;Bit 2:=1:kein odd/even-Mechan. }
$EE/ {0111: OUT DX,AL ;Neuen Wert wirksam machen }
$BA/$C4/$03/ {0112: MOV DX,03C4 ;S.o.: Sequenzer-Register 2 }
$B0/$02/ {0115: MOV AL,02 ;(=Map-Maske) }
$EE/ {0117: OUT DX,AL ;auswaehlen,... }
$42/ {0118: INC DX }
$B0/$0F/ {0119: MOV AL,0F ;...und Zugriff auf alle 4 }
$EE/ {011B: OUT DX,AL ;Bitmaps erlauben }
$B8/$00/$A0/ {011C: MOV AX,A000 ;Ab Segment $A000 }
$8E/$C0/ {011F: MOV ES,AX ;$8000 logische Woerter= }
$29/$FF/ {0121: SUB DI,DI ;4*$8000 physikalische Woerter }
$89/$F8/ {0123: MOV AX,DI ;(wg. den 4 Bitplanes) auf 0 }
$B9/$00/$80/ {0125: MOV CX,8000 ;setzen }
$FC/ {0128: CLD }
$F2/ {0129: REPNZ }
$AB/ {012A: STOSW }
$BA/$D4/$03/ {012B: MOV DX,03D4 ;CRT-Controller ansteuern }
$B0/$14/ {012E: MOV AL,14 ;Underline-location-Register }
$EE/ {0130: OUT DX,AL ;auswaehlen }
$42/ {0131: INC DX ;Wert aus zugehoerigem }
$EC/ {0132: IN AL,DX ;Datenregister auslesen }
$24/$BF/ {0133: AND AL,BF ;Bit 6:=0: keine Doppelwort- }
$EE/ {0135: OUT DX,AL ;adressierung von Daten im Bild-}
$4A/ {0136: DEC DX ;schirmspeicher durchfuehren }
$B0/$17/ {0137: MOV AL,17 ;Mode-control-Register }
$EE/ {0139: OUT DX,AL ;auswaehlen }
$42/ {013A: INC DX }
$EC/ {013B: IN AL,DX }
$0C/$40/ {013C: OR AL,40 ;Bit 6:=1: Adressierung des }
$EE {013E: OUT DX,AL ;Speichers=lineares Bitfeld }
);
Screen(1-PAGE); {sichtbar ist immer die nichtaktuelle Grafikseite}
END;
PROCEDURE Line(x1,y1,x2,y2:INTEGER; pa:BYTE);
{ in: x1,y1,x2,y2 = Koordinaten zweier Punkte, }
{ Color = Farbe (0..255) }
{ StartVirtualX,StartVirtualY = linke obere Bildschirmecke }
{ pa = Grafikseite, auf der gezeichnet werden soll (0..2) }
{out: - }
{rem: Es wurde eine Linie von den VIRTUELLEN Punkten (x1,y1) nach (x2,y2) }
{ in der Farbe COLOR gezeichnet; die Routine fuehrt dabei selber die }
{ Umrechnung der angeg. Koordinaten in absolute Bildschirmkoordinaten }
{ sowie evtl. notwendige Clipping-Schritte aus. }
{ Die Linie wird NICHT automatisch in den Hintergrund uebernommen, }
{ d.h.: sie ist nur fuer einen Animationszyklus sichtbar (soll sie }
{ permanent bleiben, so muss sie in den Hintergrund gezeichnet werden!)}
{ (Deshalb ist es sinnvoll, diese Routine NACH Aufruf von ANIMATE aus- }
{ zufuehren, da die gezeichnete Linie sonst sofort wieder verschwindet)}
CONST CodeLinks =$7; {%0111}
CodeRechts=$B; {%1011}
CodeOben =$D; {%1101}
CodeUnten =$E; {%1110}
BEGIN
IF (pa<>0) AND (pa<>1) AND (pa<>BACKGNDPAGE)
THEN Error:=Err_InvalidPageNumber
ELSE
{zuerst Linie auf sichtbaren Bereich zurechtklippen; dazu Sutherland-}
{Cohen-Algorithmus verwenden: 4 Bit-Code: links|rechts|oben|unten }
ASM
CLD
MOV CL,$F {mit %1111 anfangen}
MOV AX,x2
SUB AX,StartVirtualX {x2 in absolute Koordinaten umrechnen}
MOV x2,AX
OR AX,AX {x2<0 ?}
JL @GC1Punkt2 {ja, Flag fuer "Punkt links vom Fenster" belassen}
AND CL,CodeLinks {nein, Flag ruecksetzen}
@GC1Punkt2:
CMP AX,XMAX {x2>XMAX ?}
JG @GC2Punkt2 {ja, Flag fuer "Punkt rechts vom Fenster" belassen}
AND CL,CodeRechts {nein, Flag ruecksetzen}
@GC2Punkt2:
MOV AX,y2
SUB AX,StartVirtualY {y2 in absolute Koordinaten umrechnen}
MOV y2,AX
OR AX,AX {y2<0 ?}
JL @GC3Punkt2 {ja, Flag fuer "Punkt oberhalb des Fensters" bel.}
AND CL,CodeOben {nein, Flag ruecksetzen}
@GC3Punkt2:
CMP AX,YMAX {y2>YMAX ?}
JG @GC4Punkt2 {ja, Flag fuer "Punkt unterhalb des Fensters" bel.}
AND CL,CodeUnten
@GC4Punkt2: {CL enthaelt jetzt den Gebietscode fuer Punkt 2}
MOV AX,x1
SUB AX,StartVirtualX {x1 in absolute Koordinaten umrechnen}
MOV x1,AX
MOV AX,y1
SUB AX,StartVirtualY {y1 in absolute Koordinaten umrechnen}
MOV y1,AX
@Punkt1:
MOV CH,$F {mit %1111 anfangen}
MOV AX,x1
OR AX,AX {x1<0 ?}
JL @GC1Punkt1 {ja, Flag fuer "Punkt links vom Fenster" belassen}
AND CH,CodeLinks {nein, Flag ruecksetzen}
@GC1Punkt1:
CMP AX,XMAX {x1>XMAX ?}
JG @GC2Punkt1 {ja, Flag fuer "Punkt rechts vom Fenster" belassen}
AND CH,CodeRechts {nein, Flag ruecksetzen}
@GC2Punkt1:
MOV AX,y1
OR AX,AX {y1<0 ?}
JL @GC3Punkt1 {ja, Flag fuer "Punkt oberhalb des Fensters" bel.}
AND CH,CodeOben {nein, Flag ruecksetzen}
@GC3Punkt1:
CMP AX,YMAX {y1>YMAX ?}
JG @GC4Punkt1 {ja, Flag fuer "Punkt unterhalb des Fensters" bel.}
AND CH,CodeUnten
@GC4Punkt1: {CH enthaelt jetzt den Gebietscode fuer Punkt 1}
{CL enthaelt den Gebietscode fuer Punkt 2, CH den fuer Punkt 1}
MOV AX,CX
AND AL,AH {Code1 AND Code2 <>0 ?}
JNZ @LineReady {ja, Linie ganz ausserhalb des Windows}
MOV AX,CX
OR AL,AH {Code1 OR Code2 =0 ?}
JZ @DrawLine {ja, Linie ganz innerhalb des Windows}
{Nun eigentliches Clipping vornehmen: }
MOV AX,CX
OR AH,AH {Code1 =0 ?}
JNZ @CL3 {nein, alles ok}
MOV AX,x1 {ja, Punkte vertauschen!}
XCHG AX,x2
MOV x1,AX
MOV AX,y1
XCHG AX,y2
MOV y1,AX
XCHG CL,CH
@CL3:
MOV AL,CH {AL:=Code1}
MOV BX,x2
SUB BX,x1 {BX:=x2-x1}
MOV SI,y2
SUB SI,y1 {SI:=y2-y1}
TEST AL,NOT CodeLinks {Punkt1 links des Windows?}
JZ @CL4 {nein}
{ja, neue Koordinaten berechnen: y1:=y1+(y2-y1)/(x2-x1)*(WindowX1-X1) }
{und x1:=WindowX1 (dabei ist WindowX1 = 0) }
XOR AX,AX
XCHG AX,x1 {x1:=0}
NEG AX {AX:=-x1old}
IMUL SI
IDIV BX
ADD y1,AX
JMP @Punkt1
@CL4:
TEST AL,NOT CodeRechts {Punkt1 rechts des Windows?}
JZ @CL5 {nein}
{ja, berechne: y1:=y1+(y2-y1)/(x2-x1)*(WindowX2-X1), x1:=WindowX2 }
{ (wobei WindowX2=XMAX) }
MOV AX,XMAX
SUB AX,x1
IMUL SI
IDIV BX
ADD y1,AX
MOV x1,XMAX
JMP @Punkt1
@CL5:
TEST AL,NOT CodeOben {Punkt1 oberhalb des Windows?}
JZ @CL6 {nein}
{ja, berechne x1:=x1+(x2-x1)/(y2-y1)*(WindowY1-y1), y1:=WindowY1 }
{ (wobei WindowY1=0) }
XOR AX,AX
XCHG AX,y1
NEG AX
IMUL BX
IDIV SI
ADD x1,AX
JMP @Punkt1
@CL6:
TEST AL,NOT CodeUnten {Punkt unterhalb des Windows?}
JZ @Punkt1 {nein}
{ja, berechne x1:=x1+(x2-x1)/(y2-y1)*(WindowY2-y1), y1:=WindowY2 }
{ (wobei WindowY2=YMAX) }
MOV AX,YMAX
SUB AX,y1
IMUL BX
IDIV SI
ADD x1,AX
MOV y1,YMAX
JMP @Punkt1
{Hier gilt: die beiden Punkte wurden auf den sichtbaren Bereich zurecht-}
{gestutzt; sollte die Gerade keinen sichtbaren Teil besitzen, so wurde }
{direkt zu @LineReady verzweigt! }
@DrawLine:
PUSH BP
MOV Steigung,0 {Flag zuruecksetzen}
MOV CX,x2
SUB CX,x1 {Punkt1 rechts von Punkt2 ?}
JGE @posDX {nein}
NEG CX {ja, Punkte vertauschen}
MOV AX,x1
XCHG AX,x2
MOV x1,AX
MOV AX,y1
XCHG AX,y2
MOV y1,AX
@posDX:
MOV DI,y1
SHL DI,1
MOV DI,CS:[OFFSET gadr + DI] {DI:=y1*LINESIZE}
MOV AX,x1
MOV BL,AL
SHR AX,1
SHR AX,1
ADD DI,AX {DI:=y1*LINESIZE+(x1 DIV 4) }
AND BX,3 {BX:=(x1 AND 4) }
MOV DH,[OFFSET TranslateTab + BX] {Maske fuer VRAM-Zugriff holen}
MOV DL,2
MOV BL,pa {BH=0 -> BX=Zeichenseite}
SHL BX,1
ADD BX,OFFSET Segment_Adr -StartIndex*2
MOV ES,[BX]
{ES:DI=Zeiger auf Grafikadresse von Punkt1, DX=Zugriffsmaske dafuer}
MOV SI,LINESIZE
MOV BX,y2
SUB BX,y1 {Punkt1 unterhalb von Punkt2 ?}
JG @posDY {nein}
NEG BX {ja, deltaY und Zeileninkrement negieren}
NEG SI
@posDY:
CMP BX,CX {deltaY>deltaX ?}
JLE @flach {nein: geringe Steigung, <=1 }
XCHG BX,CX {ja, deltas vertauschen und Flag setzen}
MOV Steigung,1
{Jetzt Bresenham-Parameter berechnen: 2*DY, 2*DY-DX, 2*(DY-DX) }
@flach:
SHL BX,1
MOV DY_mal2,BX
SUB BX,CX
MOV BP,BX {BP:=2*DY-DX}
SUB BX,CX
MOV DY_m_DX_mal2,BX
INC CX {CX:=Anzahl Pixel}
MOV BL,Color
MOV BH,1
CMP Steigung,0 {steile Linie?}
JNZ @high1 {ja}
@low1: {nein}
MOV AX,3C4h
XCHG AX,DX
OUT DX,AX {richtige Bitplane anwaehlen}
MOV DX,AX {Maske wieder nach DX retten}
MOV AL,BL {Farbe fuer Punkt holen}
STOSB {Punkt setzen}
SHL DH,1 {Maske fuer naechsten Punkt berechnen }
CMP DH,16 {noch mit derselben Adresse ansprechbar? }
JE @nextbyte1 {nein, Adr. muss(te) um 1 erhoeht werden }
DEC DI {ja, Erhoehung von DI rueckgängig machen }
@low1b:
OR BP,BP
JGE @low2
ADD BP,DY_mal2
LOOP @low1
JMP @raus
@nextbyte1:
MOV DH,BH {Maske auf 1 zuruecksetzen}
JMP @low1b {Rest wie gehabt}
@low2:
ADD BP,DY_m_DX_mal2
ADD DI,SI
LOOP @low1
JMP @raus
@high1:
MOV AX,3C4h
XCHG AX,DX
OUT DX,AX
MOV DX,AX
MOV AL,BL
@high1b:
OR BP,BP
JGE @high2
ADD BP,DY_mal2
MOV ES:[DI],AL
ADD DI,SI
LOOP @high1b
JMP @raus
@high2:
ADD BP,DY_m_DX_mal2
SHL DH,1
CMP DH,16
JE @nextbyte2
MOV ES:[DI],AL
ADD DI,SI
LOOP @high1
JMP @raus
@nextbyte2:
MOV DH,BH
STOSB
ADD DI,SI
LOOP @high1
@raus:
POP BP
@LineReady:
END;
END;
PROCEDURE BackgroundLine(x1,y1,x2,y2:INTEGER);
{ in: x1,y1,x2,y2 = Koordinaten zweier Punkte, }
{ Color = Farbe (0..255) }
{ StartVirtualX,StartVirtualY = linke obere Bildschirmecke }
{out: - }
{rem: Es wurde eine Linie von den VIRTUELLEN Punkten (x1,y1) nach (x2,y2) }
{ in der Farbe COLOR in den Hintergrundspeicher gezeichnet; die Rou- }
{ tine fuehrt dabei selber alle notwendigen Umrechnungen und Clipping-}
{ schritte aus. }
{ Die Linie wird NICHT sofort sichtbar, sondern erst im naechsten Ani-}
{ mationszyklus (dann allerdings permanent)! (Deshalb ist es sinnvoll,}
{ diese Routine VOR dem Aufruf von ANIMATE auszufuehren, da dann alle }
{ Aenderungen (durch ANIMATE) sofort sichtbar werden) }
{ Da als Hintergrundseite BACKGNDADR verwendet wird, ist die Verwen- }
{ dung der Routine nur fuer den Hintergrundmodus STATIC sinnvoll! }
BEGIN
Line(x1,y1,x2,y2,BACKGNDPAGE)
END;
FUNCTION GetPixel(x,y:INTEGER):BYTE; ASSEMBLER;
{ in: x,y = VIRTUELLE Punktkoordinaten des auszulesenden Punktes}
{ PAGEADR= Grafikseite(nsegment), aus der gelesen werden soll }
{ StartVirtualX, StartVirtualY = linke obere Bildschirmecke }
{out: Farbe des Punktes}
{rem: Liegt der Punkt ausserhalb des sichtbaren Bereichs, so wird }
{ "0" als Ergebniswert zurueckgeliefert}
{ Achtung! Da PAGEADR immer die nichtsichtbare Grafikseite }
{ bezeichnet, liest diese Routine auch von dort die Punkte ein!}
ASM
XOR AL,AL {AL mit 0 vorbesetzen}
MOV DI,y
SUB DI,StartVirtualY {y in absolute Koordinaten umrechnen}
JS @offscrn
CMP DI,YMAX
JG @offscrn
MOV BX,x
SUB BX,StartVirtualX {x in absolute Koordinaten umrechnen}
JS @offscrn
CMP BX,XMAX
JG @offscrn
SHL DI,1
MOV DI,CS:[OFFSET gadr + DI]
{DI = Y*LINESIZE, BX = X, Koordinaten zulaessig}
MOV AX,BX
SHR AX,1
SHR AX,1
ADD DI,AX {DI = Y*LINESIZE+(X SHR 2) }
AND BL,3 {BL = X MOD 4 = Leseplane}
MOV AL,4
MOV AH,BL
MOV DX,3CEh
MOV ES,PAGEADR
CLI
OUT DX,AX
MOV AL,ES:[DI]
STI
@offscrn:
END;
FUNCTION BackgroundGetPixel(x,y:INTEGER):BYTE; ASSEMBLER;
{ in: x,y = VIRTUELLE Punktkoordinaten des auszulesenden Punktes}
{ StartVirtualX, StartVirtualY = linke obere Bildschirmecke }
{out: Farbe des Punktes der Hintergrundseite}
{rem: Liegt der Punkt ausserhalb des sichtbaren Bereichs, so wird }
{ "0" als Ergebniswert zurueckgeliefert}
{ Da als Hintergrundseite BACKGNDADR verwendet wird, ist die }
{ Routine nur fuer den Hintergrundmodus STATIC sinnvoll! }
ASM
XOR AL,AL {AL mit 0 vorbesetzen}
MOV DI,y
SUB DI,StartVirtualY {y in absolute Koordinaten umrechnen}
JS @offscrn
CMP DI,YMAX
JG @offscrn
MOV BX,x
SUB BX,StartVirtualX {x in absolute Koordinaten umrechnen}
JS @offscrn
CMP BX,XMAX
JG @offscrn
SHL DI,1
MOV DI,CS:[OFFSET gadr + DI]
{DI = Y*LINESIZE, BX = X, Koordinaten zulaessig}
MOV AX,BX
SHR AX,1
SHR AX,1
ADD DI,AX {DI = Y*LINESIZE+(X SHR 2) }
AND BL,3 {BL = X MOD 4 = Leseplane}
MOV AL,4
MOV AH,BL
MOV DX,3CEh
MOV ES,BACKGNDADR
CLI
OUT DX,AX
MOV AL,ES:[DI]
STI
@offscrn:
END;
FUNCTION PageGetPixel(x,y:INTEGER; pa:BYTE):BYTE; ASSEMBLER;
{ in: x,y = VIRTUELLE Punktkoordinaten des auszulesenden Punktes}
{ pa = Grafikseite (0..3), von der der Punkt ausgelesen }
{ werden soll}
{ StartVirtualX, StartVirtualY = linke obere Bildschirmecke }
{out: Farbe des Punktes der Hintergrundseite}
{rem: Liegt der Punkt ausserhalb des sichtbaren Bereichs, so wird }
{ "0" als Ergebniswert zurueckgeliefert}
{ Soll von der gerade SICHTBAREN Seite gelesen werden, so ist }
{ beim Aufruf als Seite "1-PAGE" anzugeben! }
{ Sinnvolle Werte fuer "pa" sind nur 0 und 1 (und evtl. BACK- }
{ GNDPAGE, wenn der Hintergrundmodus STATIC benutzt wird), es }
{ findet jedoch keine Ueberpruefung statt!}
ASM
XOR AL,AL {AL mit 0 vorbesetzen}
MOV DI,y
SUB DI,StartVirtualY {y in absolute Koordinaten umrechnen}
JS @offscrn
CMP DI,YMAX
JG @offscrn
MOV BX,x
SUB BX,StartVirtualX {x in absolute Koordinaten umrechnen}
JS @offscrn
CMP BX,XMAX
JG @offscrn
SHL DI,1
MOV DI,CS:[OFFSET gadr + DI]
{DI = Y*LINESIZE, BX = X, Koordinaten zulaessig}
MOV AX,BX
SHR AX,1
SHR AX,1
ADD DI,AX {DI = Y*LINESIZE+(X SHR 2) }
AND BX,3 {BL = X MOD 4 = Leseplane; BH = 0}
MOV AL,4
MOV AH,BL
MOV BL,pa {BH=0 -> BX = Grafikseite}
AND BX,3 {nur Seiten 0..3}
SHL BX,1
ADD BX,OFFSET Segment_Adr-StartIndex*2
MOV ES,[BX]
CLI
MOV DX,3CEh
OUT DX,AX
MOV AL,ES:[DI]
STI
@offscrn:
END;
PROCEDURE PutPixel(x,y:INTEGER; color:Byte); ASSEMBLER;
{ in: x,y = VIRTUELLE Punktkoordinaten des zu zeichnenden Punktes}
{ color = Farbwert fuer den zu zeichnenden Punkt}
{ 1-PAGE = Grafikseite, auf der gezeichnet werden soll}
{ StartVirtualX, StartVirtualY = linke obere Bildschirmecke}
{out: - }
{rem: Der Punkt (x,y) wurde in absolute Bildschirmkoordinaten umgerechnet }
{ und gezeichnet (sofern er auf dem sichtbaren Bildschirmfenster liegt)}
{ Der Punkt wird NICHT automatisch in den Hintergrundspeicher ueber- }
{ nommen, d.h.: er ist nur einen Animationszyklus lang sichtbar! }
{ (Deshalb ist es sinnvoll, diese Routine NACH Aufruf von ANIMATE aus- }
{ zufuehren, da der gezeichnete Punkt sonst sofort wieder verschwindet)}
ASM
MOV DI,y
SUB DI,StartVirtualY {y in absolute Koordinaten umrechnen}
JS @offscrn
CMP DI,YMAX
JG @offscrn
MOV BX,x
SUB BX,StartVirtualX {x in absolute Koordinaten umrechnen}
JS @offscrn
CMP BX,XMAX
JG @offscrn
SHL DI,1
MOV DI,CS:[OFFSET gadr + DI]
{DI = Y*LINESIZE, BX = X, Koordinaten zulaessig}
MOV AX,BX
SHR AX,1
SHR AX,1
ADD DI,AX {DI = Y*LINESIZE+(X SHR 2) }
AND BX,3
MOV AH,[OFFSET TranslateTab + BX]
MOV AL,2
MOV DX,3C4h
MOV BX,1 {ES:=Segment_Adr[1-PAGE], denn 1-PAGE=sichtbare Seite}
SUB BX,PAGE
SHL BX,1
ADD BX,OFFSET Segment_Adr-StartIndex*2
MOV ES,[BX]
CLI
OUT DX,AX
MOV AL,color
STOSB
STI
@offscrn:
END;
PROCEDURE BackgroundPutPixel(x,y:INTEGER; color:Byte); ASSEMBLER;
{ in: x,y = VIRTUELLE Punktkoordinaten des zu zeichnenden Punktes}
{ color = Farbwert fuer den zu zeichnenden Punkt}
{ StartVirtualX, StartVirtualY = linke obere Bildschirmecke}
{out: - }
{rem: Der Punkt (x,y) wurde in absolute Bildschirmkoordinaten umgerechnet und}
{ in den Hintergrund gezeichnet (sofern er im sichtbaren Bereich liegt) }
{ Der Punkt wird NICHT sofort sichtbar, sondern erst nach einem Anima- }
{ tionszyklus (dann aber permanent) (Deshalb ist es sinnvoll, diese Rou- }
{ tine VOR dem Aufruf von ANIMATE auszufuehren, so dass evtl. Aenderungen}
{ des Hintergrundes "sofort" sichtbar werden!) }
{ Da als Hintergrundseite BACKGNDADR verwendet wird, ist die Verwendung }
{ der Routine nur fuer den Hintergrundmodus STATIC sinnvoll!}
ASM
MOV DI,y
SUB DI,StartVirtualY {y in absolute Koordinaten umrechnen}
JS @offscrn
CMP DI,YMAX
JG @offscrn
MOV BX,x
SUB BX,StartVirtualX {x in absolute Koordinaten umrechnen}
JS @offscrn
CMP BX,XMAX
JG @offscrn
SHL DI,1
MOV DI,CS:[OFFSET gadr + DI]
{DI = Y*LINESIZE, BX = X, Koordinaten zulaessig}
MOV AX,BX
SHR AX,1
SHR AX,1
ADD DI,AX {DI = Y*LINESIZE+(X SHR 2) }
AND BX,3
MOV AH,[OFFSET TranslateTab + BX]
MOV AL,2
MOV DX,3C4h
MOV ES,BACKGNDADR
CLI
OUT DX,AX
MOV AL,color
STOSB
STI
@offscrn:
END;
PROCEDURE PagePutPixel(x,y:INTEGER; color,pa:Byte); ASSEMBLER;
{ in: x,y = VIRTUELLE Punktkoordinaten des zu zeichnenden Punktes}
{ color = Farbwert fuer den zu zeichnenden Punkt}
{ pa = Grafikseite (0..3), auf der gezeichnet werden soll }
{ PAGEADR= Grafikseite(nsegment), auf der gezeichnet werden soll}
{ StartVirtualX, StartVirtualY = linke obere Bildschirmecke}
{out: - }
{rem: Der Punkt (x,y) wurde in absolute Bildschirmkoordinaten umgerechnet }
{ und gezeichnet (sofern er auf dem sichtbaren Bildschirmfenster liegt)}
{ Soll auf die gerade SICHTBARE Seite gezeichnet werden, so ist}
{ beim Aufruf als Seite "1-PAGE" anzugeben! }
{ Auch hier gilt, dass der gezeichnete Punkt NICHT automatisch }
{ in den Hintergrundspeicher uebernommen wird, d.h.: er ist nur}
{ bis zum naechsten Animationszyklus (= Aufruf von ANIMATE) }
{ sichtbar! (Deshalb ist es sinnvoll, diese Routine NACH Aufruf}
{ von ANIMATE auszufuehren, da der gezeichnete Punkt sonst so- }
{ fort wieder verschwindet!) }
{ Sinnvolle Werte fuer "pa" sind nur 0 und 1 (und evtl. BACK- }
{ GNDPAGE, wenn der Hintergrundmodus STATIC benutzt wird), es }
{ findet jedoch keine Ueberpruefung statt!}
ASM
MOV DI,y
SUB DI,StartVirtualY {y in absolute Koordinaten umrechnen}
JS @offscrn
CMP DI,YMAX
JG @offscrn
MOV BX,x
SUB BX,StartVirtualX {x in absolute Koordinaten umrechnen}
JS @offscrn
CMP BX,XMAX
JG @offscrn
SHL DI,1
MOV DI,CS:[OFFSET gadr + DI]
{DI = Y*LINESIZE, BX = X, Koordinaten zulaessig}
MOV AX,BX
SHR AX,1
SHR AX,1
ADD DI,AX {DI = Y*LINESIZE+(X SHR 2) }
AND BX,3
MOV AH,[OFFSET TranslateTab + BX]
MOV AL,2
MOV DX,3C4h
MOV BL,pa {BH=0 -> BX=Grafikseite}
SHL BX,1
ADD BX,OFFSET Segment_Adr+StartIndex*2
MOV ES,[BX]
CLI
OUT DX,AX
MOV AL,color
STOSB
STI
@offscrn:
END;
PROCEDURE OutTextXY(x,y:INTEGER; pa:BYTE; s:STRING);
{ in: (x,y) = (virtuelle) Startkoordinaten des auszugebenden Textes}
{ s = auszugebender Textstring }
{ pa = Grafikseite, auf der der Text ausgegeben werden soll }
{ GraphTextColor=Textfarbe }
{ GraphTextBackground=Farbe fuer Texthintergrund;ist dieser Wert}
{ =GraphTextColor, so werden nur die Textpixel gezeichnet}
{ und die umgebenden Pixel unveraendert gelassen (=nor- }
{ males Verhalten von TurboPascal's OutText-Routinen!) }
{ GraphTextOrientation="vertical" oder "horizontal" }
{ StartVirtualX,StartVirtualY = linke obere Bildschirmecke }
{out: Text wurde auf dem Bildschirm ausgegeben }
VAR z,b,bit,i:BYTE;
data:Fontchar;
BEGIN
IF (pa<>0) AND (pa<>1) AND (pa<>BACKGNDPAGE)
THEN BEGIN
Error:=Err_InvalidPageNumber;
exit
END;
FOR i:=1 TO Length(s) DO
BEGIN
data:=FontData[ord(s[i])];
FOR z:=0 TO FontHeight-1 DO
BEGIN
b:=data[z];
FOR bit:=0 TO FontWidth-1 DO
IF b and FontMask[bit]<>0
THEN PagePutPixel(x+bit,y+z,GraphTextColor,pa)
ELSE IF (GraphTextColor<>GraphTextBackground)
THEN PagePutPixel(x+bit,y+z,GraphTextBackground,pa);
END;
IF GraphTextOrientation=horizontal
THEN INC(x,FontWidth)
ELSE INC(y,FontHeight);
END;
END;
PROCEDURE BackgroundOutTextXY(x,y:INTEGER; s:STRING);
{rem: Wie OutTextXY(), aber es wird in den Hintergrund geschrieben und}
{ nicht in die durch PAGEADR spezifizierte Seite! }
{ Da als Hintergrundseite BACKGNDADR verwendet wird, ist die }
{ Routine nur fuer den Hintergrundmodus STATIC sinnvoll!}
VAR z,b,bit,i:BYTE;
data:Fontchar;
BEGIN
FOR i:=1 TO Length(s) DO
BEGIN
data:=FontData[ord(s[i])];
FOR z:=0 TO FontHeight-1 DO
BEGIN
b:=data[z];
FOR bit:=0 TO FontWidth-1 DO
IF b and FontMask[bit]<>0
THEN BackgroundPutPixel(x+bit,y+z,GraphTextColor)
ELSE IF (GraphTextColor<>GraphTextBackground)
THEN BackgroundPutPixel(x+bit,y+z,GraphTextBackground);
END;
IF GraphTextOrientation=horizontal
THEN INC(x,FontWidth)
ELSE INC(y,FontHeight);
END;
END;
FUNCTION Hitdetect(s1,s2:INTEGER):BOOLEAN; ASSEMBLER;
{ in: s1,s2 = Spritepositionsnummern zweier Sprites}
{ SpriteN[s1],SpriteX[s1],SpriteY[s1] = Spritedaten von Sprite s1 }
{ SpriteN[s2],SpriteX[s2],SpriteY[s2] = Spritedaten von Sprite s2 }
{out: TRUE/FALSE fuer "Sprites kollidieren"/"Sprites kollidieren nicht" }
{rem: Diese Ueberpruefung geschieht punktgenau und ist unabhaengig davon,}
{ ob die Sprites z.Z. gerade sichtbar sind oder nicht. }
{ Inaktive Sprites (SpriteN[s?]=0) koennen nicht miteinander kollid. }
{ Ein Sprite kann nicht mit sich selbst kollidieren (s1=s2 -> FALSE) }
ASM
MOV SI,s1 {1.Parameter s1 vom Stack holen}
MOV DI,s2 {2.Parameter s2 vom Stack holen}
CMP SI,DI
JE @NOHIT1 {Sprite kann sich nicht selbst treffen}
SHL SI,1
mov cx,[SI + OFFSET SpriteN]
jcxz @NOHIT1 {Sprite <>0, d.h.: ueberhaupt aktiv?}
SHL DI,1
MOV BX,[DI + OFFSET SpriteN]
OR BX,BX {dto. fuer anderes Sprite}
JNE @PRUEF2
@NOHIT1:
JMP @NOHIT7 {inaktive Sprites koennen auch nicht}
{kollidieren -> FALSE zurueckgeben }
{hier: SI (DI) = Zeiger auf 1. (2.) Sprite in ?WRTD[..] ,}
{ CX (BX) = Spritenummer von Sprite 1 (2) }
{(etwas spaeter wird dann DS (ES) = Segment der Spr.daten von Spr.1 (2) )}
@PRUEF2:
MOV AX,[SI + OFFSET SpriteY]
MOV DX,[DI + OFFSET SpriteY]
mov si,[SI + OFFSET SpriteX] {SI=x1}
mov di,[DI + OFFSET SpriteX] {DI=x2}
shl bx,1 {BX=Spritenummer2*2}
mov es,[BX + OFFSET SPRITEAD] {ES=Segment der Spritedaten2}
mov bx,cx {(CX=Spritenummer1)}
shl bx,1 {BX=Spritenummer1*2}
MOV ds,[BX + OFFSET SPRITEAD]
mov [y1],ax
mov [y2],dx
sub dx,ax
mov CS:WORD PTR @y2_y1+1,dx
mov [x1],si
mov [x2],di
mov dx,di
sub dx,si
mov CS:WORD PTR @x2_x1+1,dx
mov ax,es:[Left] {AX=Zeiger auf linke Randdaten}
mov CS:WORD PTR @lirand2+1,ax
mov ax,es:[Right] {AX=Zeiger auf rechte Randdaten}
mov CS:WORD PTR @rerand2+1,ax
mov ax,es:[Top] {AX=Zeiger auf obere Randdaten}
mov CS:WORD PTR @orand2+1,ax
mov ax,es:[Bottom] {AX=Zeiger auf untere Randdaten}
mov CS:WORD PTR @urand2+1,ax
mov ax,es:[Breite] {AX=max. Breite in 4er-Gruppen}
shl al,1
shl al,1
mov CS:WORD PTR @breite2+1,ax {*4 = Breite in Punkten}
mov ax,es:[Hoehe]
mov CS:WORD PTR @hoehe2+1,ax {Hoehe von Sprite2 in Punkten}
MOV AX,[Left] {AX=Zeiger auf linke Randdaten}
MOV CS:WORD PTR @LIRAND1+1,AX
MOV AX,[Right] {AX=Zeiger auf rechte Randdaten}
MOV CS:WORD PTR @RERAND1+1,AX
MOV AX,[Top] {AX=Zeiger auf obere Randdaten}
MOV CS:WORD PTR @ORAND1+1,AX
MOV AX,[Bottom] {AX=Zeiger auf untere Randdaten}
MOV CS:WORD PTR @URAND1+1,AX
MOV BX,[Breite] {BX=max. Breite in 4er-Gruppen}
SHL BX,1
SHL BX,1 {*4 = Breite in Punkten}
MOV CS:WORD PTR @BREITE1+2,BX
lea bx,[si+bx-1] {BX:=x1+breite1-1 (=x1last)}
@breite2:
mov bp,1234h {Dummywert}
mov cx,bp {CX=breite2 brauchen wir spaeter nochmal}
lea bp,[di+bp-1] {BP:=x2+breite2-1 (=x2last)}
cmp bx,bp
jle @noex1
mov bp,bx
@noex1: {hier: BP=max(x1last,x2last) (=maxx)}
cmp si,di
jle @X1_klgl_X2
xchg si,di
@X1_klgl_X2: {hier: SI=min(x1,x2) (=minx)}
stc
sbb si,bp {SI:=minx-maxx-1=-(maxx-minx+1)}
@breite1:
add cx,1234h {(Dummywert) CX:=breite1+breite2}
add cx,si {CX:=breite1+breite2-(maxx-minx+1)}
dec cx {CX:=breite1+breite2-(maxx-minx+1)-1 (=ueberlappx-1)}
js @NOHIT2 {kein Treffer, wenn ueberlappx<=0}
mov [ueberlappx_1],cx
mov ax,[Hoehe]
mov bx,ax {BX:=hoehe1}
mov di,[y1] {DI:=y1}
add ax,di {AX:=y1+hoehe1}
dec ax {AX:=y1+hoehe1-1 (=y1last)}
@hoehe2:
mov si,1234h
mov dx,[y2]
add dx,si {DX:=y2+hoehe2}
dec dx {DX:=y2+hoehe2-1 (=y2last)}
cmp ax,dx
jge @noex2
mov ax,dx
@noex2: {hier: AX=max(y1last,y2last) (=maxy)}
mov dx,[y2]
cmp di,dx {(DI=y1)}
jle @noex3
mov di,dx
@noex3: {hier: DI=min(y1,y2) (=miny)}
sub di,ax {DI:=miny-maxy=-(maxy-miny)}
lea ax,[bx+si-2] {AX:=hoehe1+hoehe2-2}
add ax,di {AX:=hoehe1+hoehe2-(maxy-miny+1)-1 (=ueberlappy-1)}
js @NOHIT2 {kein Treffer, wenn ueberlappy<=0}
mov [ueberlappy_1],ax
{hier: AX=ueberlappy-1, CX=ueberlappx-1}
@x2_x1:
mov dx,1234h {Dummywert}
xor bx,bx {ab jetzt: BX=0 !}
or dx,dx
js @X2_X1_kl_0 {if x2-x1>=0 then...}
mov [hit2xfirst],bx {...hit2xfirst:=0}
mov [hit1xfirst],dx {...hit1xfirst:=x2-x1}
jmp @Yhits {SHORT}
{Sprungleiste fuer NOHIT (passt hier gut hin)}
@NOHIT2:
JMP @NOHIT7
{jetzt wieder normales Programm}
@X2_X1_kl_0: {else (x2-x1<0)...}
mov [hit1xfirst],bx {...hit1xfirst:=0}
neg dx {DX:=x1-x2}
mov [hit2xfirst],dx {...hit2xfirst:=x1-x2}
@Yhits: {hier: AX=ueberlappy-1}
@y2_y1:
mov dx,1234h {Dummywert}
or dx,dx
js @Y2_Y1_kl_0 {if y2-y1>=0 then...}
mov [hit2yfirst],bx {...hit2yfirst:=0}
mov [hit1yfirst],dx {...hit1yfirst:=y2-y1}
jmp @iterate {SHORT}
@Y2_Y1_kl_0: {else (y2-y1<0)...}
mov [hit1yfirst],bx {...hit1yfirst:=0}
neg dx {DX:=y1-y2}
mov [hit2yfirst],dx {...hit2yfirst:=y1-y2}
{Nun werden iterativ die ueberlappenden Zeilen und Spalten exakt geprueft}
@iterate:
mov cx,[ueberlappy_1] {Anzahl der zu vergleichenden Zeilen -1}
shl cx,1 {*2, da Word-Werte!}
@lirand1:
mov si,1234h {Dummywert}
@lirand2:
mov di,1234h {Dummywert}
@rerand1:
mov bx,1234h {Dummywert}
@rerand2:
mov bp,1234h {Dummywert}
sub bx,si {BX:=rerand1-lirand1}
sub bp,di {BP:=rerand2-lirand2}
mov ax,[hit1yfirst]
shl ax,1
add si,ax {SI:=1.Zeile, in der Sp.1 mit Sp.2 ueberlappt}
mov ax,[hit2yfirst]
shl ax,1
add di,ax {DI:=1.Zeile, in der Sp.2 mit Sp.1 ueberlappt}
add si,cx {dto., letzte Zeile}
add di,cx
@one_line:
mov ax,[si] {DS:AX:=x1li[Zeile]}
mov dx,es:[di] {ES:DX:=x2li[Zeile]}
add ax,[x1] {AX:=x1li[Zeile]+x1 (=c)}
add dx,[x2] {DX:=x2li[Zeile]+x2 (=d)}
cmp ax,dx
jge @C_grgl_D
mov ax,dx
@C_grgl_D: {hier: AX=max(c,d)}
mov cx,[si+bx] {DS:CX:=x1re[Zeile]}
mov dx,es:[di+bp] {ES:DX:=x2re[Zeile]}
add cx,[x1] {CX:=x1re[Zeile]+x1 (=a)}
add dx,[x2] {DX:=x2re[Zeile]+x2 (=b)}
cmp cx,dx
jle @A_klgl_B
mov cx,dx
@A_klgl_B: {hier: CX=min(a,b)}
cmp cx,ax {min(a,b)>=max(c,d) ?}
jge @found_Xhit {ja: Treffer in X-Richtung gefunden!}
dec si {naechste Zeile (-> Word-Werte!)}
dec si
dec di
dec di
dec WORD PTR [ueberlappy_1]
jns @one_line
{kein Treffer in X-Richtung -> ueberhaupt kein Treffer!}
jmp @NOHIT7
{ansonsten: Treffer in X-Ri., jetzt noch Y-Ri. pruefen (analog zu oben) und }
{"Treffer!" nur dann ausgeben, falls auch mind. 1 Treffer in Y-Ri. existiert}
@found_Xhit:
mov cx,[ueberlappx_1] {Anzahl der zu vergleichenden Spalten -1}
shl cx,1 {*2, da Word-Werte!}
@orand1:
mov si,1234h {Dummywert}
@orand2:
mov di,1234h {Dummywert}
@urand1:
mov bx,1234h {Dummywert}
@urand2:
mov bp,1234h {Dummywert}
sub bx,si {BX:=urand1-orand1}
sub bp,di {BP:=urand2-orand2}
mov ax,[hit1xfirst]
shl ax,1 {*2, da Word-Werte!}
add si,ax {SI:=orand1+2*hit1xfirst}
mov ax,[hit2xfirst]
shl ax,1 {*2, da Word-Werte!}
add di,ax {DI:=orand2+2*hit2xfirst}
add si,cx
add di,cx
@one_column: mov ax,[si] {AX:=y1ob[Spalte]}
cmp ax,16000 {Dummywert fuer "leere Spalte"?}
je @next_column {ja, also sicherlich kein Treffer}
mov dx,es:[di] {DX:=y2ob[Spalte]}
cmp dx,16000 {auch 2.Sprite pruefen: "leere Spalte"?}
je @next_column {ja, kein Treffer}
add ax,[y1] {AX:=y1ob+y1 (=c)}
add dx,[y2] {DX:=y2ob+y2 (=d)}
cmp ax,dx
jge @C_grgl_D2
mov ax,dx
@C_grgl_D2: {hier: AX=max(c,d)}
mov cx,[si+bx] {DS:CX:=y1un[Spalte]}
mov dx,es:[di+bp] {ES:DX:=y2un[Spalte]}
add cx,[y1] {CX:=y1un+y1 (=a)}
add dx,[y2] {DX:=y2un+y2 (=b)}
cmp cx,dx
jle @A_klgl_B2
mov cx,dx
@A_klgl_B2: {hier: CX=min(a,b)}
cmp cx,ax {min(a,b)>=max(c,d) ?}
jge @HIT2 {ja: Treffer gefunden!}
@next_column:
dec si {nein, naechste Spalte (-> Word-Werte!)}
dec si
dec di
dec di
dec WORD PTR [ueberlappx_1]
jns @one_column
@NOHIT7:
XOR AX,AX {als Ergebnis 0 = FALSE zurueckgeben}
JMP @TREFF_END {SHORT}
@HIT2:
MOV AX,1 {als Ergebnis 1 = TRUE zurueckgeben}
@TREFF_END:
mov dx,seg @DATA {BP wird von TP wiederhergestellt}
mov ds,dx
END;
PROCEDURE Animate;
{ in: PAGEADR = aktuelle Grafikseite(nadresse),auf der gezeichnet werden soll}
{ BACKGNDADR = Hintergrundseite(nadresse) }
{ BACKGROUNDMODE = STATIC/SCROLLING fuer festen/scrollbaren Hintergrund }
{ SpriteN[] = Spritenummern der darzustellenden Sprites }
{ SpriteX[],SpriteY[] = deren zugehoergigen (virtuellen) Koordinaten}
{ StartVirtualX,StartVirtualY = obere linke Bildschirmecke }
{ (PAGE = aktuell dargestellte Grafikseite) }
{out: PAGE = 0/1, wenn PAGE vorher 1/0 war }
{ PAGEADR = neue, aktuelle Grafikseite(nadresse) }
{rem: Animate loescht den Inhalt der alten Grafik (mithilfe der Hintergrund- }
{ seite), zeichnet alle sichtbaren Sprites, synchronisiert auf das vert. }
{ Retracesignal und schaltet dann auf die so fertiggestellte Seite um. }
VAR offsetXTiles,offsetYTiles,offsetXPix,offsetYPix:INTEGER;
leftcut,rightcut,topcut,bottomcut,tiles:WORD;
x,y,xpix,ypix,xtil,ytil,actindex,randindex,index:INTEGER;
offscreenFlag:BYTE;
yt,xt:INTEGER;
BEGIN
ASM
CLD
{zuerst das Hintergrundbild auf die aktuelle Seite kopieren:}
CMP BackgroundMode,STATIC {welcher Hintergrundmodus?}
JE @static_bckgnd
JMP @scrolling_bckgnd
@static_bckgnd:
MOV AX,0F02h {alle 4 Planes gleichzeitig beschreiben}
MOV DX,3C4h
OUT DX,AX
MOV AX,4105h {Schreibmodus 1 waehlen}
MOV DX,3CEh
OUT DX,AX
MOV ES,PAGEADR {Grafikseite mit Hintergrundmuster fuellen}
MOV DS,BACKGNDADR
XOR SI,SI
MOV DI,SI
MOV CX,PAGESIZE
REP MOVSB
MOV AX,SEG @DATA
MOV DS,AX
MOV AX,4005h {Writemode 0 setzen}
MOV DX,3CEh
OUT DX,AX
JMP @Sprites_zeichnen
{---------------------------------}
@scrolling_bckgnd: {ab hier: Hintergrund aus Kacheln zusammensetzen}
MOV AX,StartVirtualY
MOV BX,AX {AX=BX=StartVirtualY}
SUB AX,BackY1
ADD AX,15 {offsetYTiles:=(StartVirtualY-BackY1+15)DIV16}
SAR AX,1
SAR AX,1
SAR AX,1
SAR AX,1
MOV offsetYTiles,AX
MOV ytil,AX {ytil:=offsetYTiles}
DEC AX
IMUL XTiles
DEC AX
MOV actIndex,AX {actIndex:=(ytil-1)*XTiles-1, "+xtil" kommt spaeter}
MOV AX,16
SUB AX,BX {BX=StartVirtualY}
AND AX,$F
MOV ypix,AX {ypix:=(16-StartVirtualY) AND $F}
SUB AX,200
AND AX,$F
MOV bottomcut,AX {bottomcut:=(ypix-200) AND $F}
AND BX,$F {offsetYPix:=topcut:=StartVirtualY AND $F}
MOV topcut,BX
MOV offsetYPix,BX
MOV AX,StartVirtualX
MOV BX,AX {AX=BX=StartVirtualX}
SUB AX,BackX1
ADD AX,15 {offsetXTiles:=(StartVirtualX-BackX1+15)DIV16}
SAR AX,1
SAR AX,1
SAR AX,1
SAR AX,1
MOV offsetXTiles,AX
MOV xtil,AX {xtil:=offsetXTiles}
ADD actIndex,AX {actIndex:=(ytil-1)*XTiles-1+xtil}
MOV AX,16
SUB AX,BX {BX=StartVirtualX}
AND AX,$F
MOV xpix,AX {xpix=rightcut:=(16-StartVirtualX) AND $F}
MOV rightcut,AX
AND BX,$F {offsetXPix:=leftcut:=StartVirtualX AND $F}
MOV leftcut,BX
MOV offsetXPix,BX
MOV AX,(XMAX+1)/16-1
SUB BL,1 {C=1, wenn leftcut=0}
ADC AX,0
MOV tiles,AX {tiles:=19+ord(leftcut=0)}
CMP topcut,0 {wenn topcut=0 ist, braucht die oberste}
JE @do_innertiles {Tilezeile nicht gesondert gezeichnet werden}
{oberste Tilezeile:}
MOV DX,xtil
MOV xt,DX
MOV AX,ytil
DEC AX
MOV yt,AX
MOV CL,1
JS @offscreen
CMP AX,YTiles
JAE @offscreen
DEC CL
@offscreen: {CL=0/1 fuer offscreenFlag=false/true=(yt<0) OR (yt>=YTiles}
MOV offscreenFlag,CL
CMP leftcut,0 {wenn leftcut=0 ist, muss die linke obere}
JE @do_upperinnertiles {Ecke nicht gesondert gezeichnet werden }
{linke obere Eck-Tile zeichnen:} {CL=offscreenFlag, DX=xt}
XOR SI,SI {Tileindex bestimmen:}
DEC CL {IF offscreenFlag OR (xt-1<0) OR (xt-1>=XTiles) }
JZ @go1 { THEN index:=0 ELSE index:=actIndex}
DEC DX
JS @go1
CMP DX,XTiles
JAE @go1
MOV SI,actIndex {=yt*XTiles+(xt-1)}
@go1:
{PROCEDURE DrawUpperLeftTile(leftcut,topcut:INTEGER; index:WORD);}
{ in: leftcut = Anzahl der links abzuschneidenden Tile-Spalten}
{ topcut = dto., oben}
{ SI = index = Tilenummer}
{out: Tile wurde auf aktueller Seite PAGEADR bei (0,0) gezeichnet}
{rem: Tile wurde links und oben entsprechend geschnitten}
{ leftcut muss zwischen 0 und 15 liegen, 16 ist erlaubt (aber unsinnig)}
{ topcut muss zwischen 0 und 15 liegen, 16 ist verboten}
MOV AH,[OFFSET BackTile +SI] {AH:=BackTile[index]}
XOR AL,AL
SHR AH,1 {AX:=Kachel*64 =Kachel SHL 6 =(Kachel SHL 8) SHR 2}
RCR AL,1 {deshalb: AH:=Kachel und anschliessend AX um 2 Bit}
SHR AH,1 {rechtsschieben!}
RCR AL,1
MOV SI,topcut
MOV CX,16
SUB CX,SI {CX:=16-topcut = Anzahl zu zeichnende Zeilen}
SHL SI,1
SHL SI,1 {fuer jede oben abgeschnittene Zeile 4 Bytes}
ADD SI,AX {zur (Offset-)Quelladresse in Page 3 addieren}
XOR DI,DI {die erste Zieladresse ist DI:=0*LINESIZE+(0 div 4)=0}
MOV AX,leftcut
MOV BX,AX {Kopie von leftcut in BX aufbewahren}
SHR AX,1
SHR AX,1
ADD SI,AX {SI um cutoff in Bytes weitersetzen=leftcut div 4}
{Jetzt wird keine Variable aus dem Stack mehr gebraucht: BP kann}
{verwendet werden!}
PUSH BP {wird beim Verlassen der Prozedur gebraucht!}
MOV BP,16+3
SUB BP,BX {BP:=16+3-leftcut, denn die Zahl der Bytes je Zeile fuer}
{Plane i berechnet sich zu (16+3-i-leftcut) SHR 2 }
MOV ES,PAGEADR {(Segment-)Zieladresse ist aktive Grafikseite}
MOV DS,SCROLLADR {(Segment-)Quelladresse ist die Seite SCROLLPAGE}
MOV DX,3CEh
AND BL,3
MOV AH,BL {AH:=leftcut mod 4}
MOV AL,4
JNE @mode0a {nur falls leftcut mod 4=0 ist koennen wir WriteMode1 verwenden}
{--- "Abkuerzung" ueber WriteMode 1 moeglich!: DX=3CEh, BP=16+3-leftcut}
{ Dass BP "3 zu gross ist" stoert nicht, da dieser Code nur ausge- }
{ fuehrt wird, wenn leftcut mod 4=0, also 16+3-leftcut=...11b, die }
{ "11b" werden also beim Rechtsschieben eh abgeschnitten!}
MOV AX,4105h
OUT DX,AX {WriteMode 1 waehlen}
MOV DX,3C4h
MOV AX,0F02h {alle 4 Planes gleichzeitig bearbeiten}
OUT DX,AX
JMP @lastPlane1 {abkuerzen}
{---}
@mode0a:
OUT DX,AX {aktuelle Leseplane waehlen}
PUSH AX {und fuer spaeter merken}
MOV DX,3C4h
MOV AX,0102h {Schreibplane 0 waehlen}
OUT DX,AX
MOV BX,BP
SHR BX,1
SHR BX,1 {BX:=Bytes_je_Plane0_Zeile = (16+3-leftcut) DIV 4}
MOV AX,LINESIZE {Korrekturfaktor fuer Zeilenzieladressen}
SUB AX,BX { := LINESIZE-Bytes_je_Plane0_Zeile}
MOV DX,4 {Korrekturfaktor fuer Tilezeilenquelladressen}
SUB DX,BX { := 4-Bytes_je_Plane0_Zeile}
PUSH SI {Quell- und Zieladresse fuer weitere Planes retten}
PUSH DI
PUSH BP {BP retten}
PUSH CX {CX = Zeilenzaehler retten}
MOV BP,CX {BP:=Zeilenzaehler}
@eineZeile1a:
MOV CX,BX {CX:=Bytes_je_Plane0_Zeile}
REP MOVSB {eine Zeile uebertragen}
ADD DI,AX {DI auf naechste Zielzeile setzen}
ADD SI,DX {SI auf naechste Tilequellzeile setzen}
DEC BP
JNZ @eineZeile1a
POP CX
POP BP
POP DI
POP SI
MOV DX,3C4h
MOV AX,0202h {Schreibplane 1 waehlen}
OUT DX,AX
MOV DX,3CEh {naechste Leseplane:}
POP AX
INC AH
AND AH,3 {um 1 erhoehen MOD 4}
JNE @nowrap1a
INC SI {nach Plane 3 kommt wieder Plane 0, aber}
{die Quelladresse hat sich um 1 Byte erhoeht}
@nowrap1a:
OUT DX,AX
PUSH AX
DEC BP {BP:=16+2-leftcut}
MOV BX,BP
SHR BX,1
SHR BX,1 {BX:=Bytes_je_Plane1_Zeile}
MOV AX,LINESIZE {Korrekturfaktor fuer Zeilenzieladressen}
SUB AX,BX { := LINESIZE-Bytes_je_Plane1_Zeile}
MOV DX,4 {Korrekturfaktor fuer Tilezeilenquelladressen}
SUB DX,BX { := 4-Bytes_je_Plane1_Zeile}
PUSH SI {Quell- und Zieladresse fuer weitere Planes retten}
PUSH DI
PUSH BP {BP retten}
PUSH CX {Zeilenzaehler retten}
MOV BP,CX {BP:=Zeilenzaehler}
@eineZeile2a:
MOV CX,BX {CX:=Bytes_je_Plane0_Zeile}
REP MOVSB {eine Zeile uebertragen}
ADD DI,AX {DI auf naechste Zielzeile setzen}
ADD SI,DX {SI auf naechste Tilequellzeile setzen}
DEC BP
JNZ @eineZeile2a
POP CX
POP BP
POP DI
POP SI
MOV DX,3C4h
MOV AX,0402h {Schreibplane 2 waehlen}
OUT DX,AX
MOV DX,3CEh {naechste Leseplane:}
POP AX
INC AH
AND AH,3 {um 1 erhoehen MOD 4}
JNE @nowrap2a
INC SI {nach Plane 3 kommt wieder Plane 0, aber}
{die Quelladresse hat sich um 1 Byte erhoeht}
@nowrap2a:
OUT DX,AX
PUSH AX
DEC BP {BP:=16+1-leftcut}
MOV BX,BP
SHR BX,1
SHR BX,1 {BX:=Bytes_je_Plane2_Zeile}
MOV AX,LINESIZE {Korrekturfaktor fuer Zeilenzieladressen}
SUB AX,BX { := LINESIZE-Bytes_je_Plane2_Zeile}
MOV DX,4 {Korrekturfaktor fuer Tilezeilenquelladressen}
SUB DX,BX { := 4-Bytes_je_Plane2_Zeile}
PUSH SI {Quell- und Zieladresse fuer weitere Planes retten}
PUSH DI
PUSH BP {BP retten}
PUSH CX {Zeilenzaehler retten}
MOV BP,CX {BP:=Zeilenzaehler}
@eineZeile3a:
MOV CX,BX {CX:=Bytes_je_Plane0_Zeile}
REP MOVSB {eine Zeile uebertragen}
ADD DI,AX {DI auf naechste Zielzeile setzen}
ADD SI,DX {SI auf naechste Tilequellzeile setzen}
DEC BP
JNZ @eineZeile3a
POP CX
POP BP
POP DI
POP SI
MOV DX,3C4h
MOV AX,0802h {Schreibplane 3 waehlen}
OUT DX,AX
MOV DX,3CEh {naechste Leseplane:}
POP AX
INC AH
AND AH,3 {um 1 erhoehen MOD 4}
JNE @nowrap3a
INC SI {nach Plane 3 kommt wieder Plane 0, aber}
{die Quelladresse hat sich um 1 Byte erhoeht}
@nowrap3a:
OUT DX,AX
DEC BP {BP:=16-leftcut}
@lastplane1:
{BP direkt verwenden}
SHR BP,1
SHR BP,1 {BP:=Bytes_je_Plane3_Zeile}
MOV AX,LINESIZE {Korrekturfaktor fuer Zeilenzieladressen}
SUB AX,BP { := LINESIZE-Bytes_je_Plane3_Zeile}
MOV DX,4 {Korrekturfaktor fuer Tilezeilenquelladressen}
SUB DX,BP { := 4-Bytes_je_Plane3_Zeile}
{Quell- und Zieladresse nicht mehr retten}
MOV BX,CX {BX:=Zeilenzaehler}
@eineZeile4a:
MOV CX,BP {CX:=Bytes_je_Plane0_Zeile}
REP MOVSB {eine Zeile uebertragen}
ADD DI,AX {DI auf naechste Zielzeile setzen}
ADD SI,DX {SI auf naechste Tilequellzeile setzen}
DEC BX
JNZ @eineZeile4a
{--- falls Abkuerzung genommen wurde, muss WriteMode 0 wieder}
{ gesetzt werden; hier der Einfachheit immer neugesetzt }
MOV AX,4005h
MOV DX,3CEh
OUT DX,AX
{---}
POP BP
MOV AX,SEG @Data {DS wiederherstellen}
MOV DS,AX
{Nun alle anderen oberen Kacheln, die nur oben (aber nicht seitlich)}
{abgeschnitten sind: }
@do_upperinnertiles:
MOV AX,xpix
MOV x,AX
INC actIndex
@repeat1: {Schleife wird genau "tiles" mal durchlaufen}
MOV CL,offscreenFlag
XOR SI,SI {Tileindex bestimmen:}
DEC CL {IF offscreenFlag OR (xt<0) OR (xt>=XTiles) }
JZ @go2 { THEN index:=0 ELSE index:=actIndex =yt*XTiles+xt}
MOV DX,xt
OR DX,DX
JS @go2
CMP DX,XTiles
JAE @go2
MOV SI,actIndex
@go2:
{PROCEDURE DrawUpperTile(x,topcut:INTEGER; index:WORD);}
{ in: (x,0) = linke obere Ecke der zu zeichnenden Tile,}
{ topcut= Anzahl abzuschneidende Zeilen}
{ SI = index = Tilenummer}
{out: Tile wurde auf aktueller Seite PAGEADR gezeichnet}
{rem: Tile muss links, rechts (und unten) ganz auf dem Bildschirm sein}
{ topcut muss im Stapel liegen (da DS andersweitig belegt wird!}
{ topcut muss Werte zwischen 0..15 haben, 16 ist unzulaessig!}
MOV AH,[OFFSET BackTile +SI] {AH:=BackTile[index]}
XOR AL,AL
SHR AH,1 {AX:=Kachel*64 =Kachel SHL 6 =(Kachel SHL 8) SHR 2}
RCR AL,1 {deshalb: AH:=Kachel und anschliessend AX um 2 Bit}
SHR AH,1 {rechtsschieben!}
RCR AL,1 {Das ist zugleich die (Offset-)Quelladresse in Page 3}
MOV SI,topcut {Dazu kommen die oben abgeschnittenen Zeilen:}
MOV CX,16 {fuer jede Zeile 4 Bytes}
SUB CX,SI {CX:=16-topcut = zu zeichnende Zeilen}
SHL SI,1
SHL SI,1
ADD SI,AX {SI = Zeiger auf erstes zu kopierendes Tilebyte}
{Die erste Zieladresse ist DI:=0*LINESIZE+(x div 4) = x div 4}
MOV DI,x
MOV BX,DI {Kopie von X in BX aufbewahren}
SHR DI,1
SHR DI,1
{Jetzt wird keine Variable aus dem Stack mehr gebraucht: BP kann}
{verwendet werden!}
PUSH BP {wird beim Verlassen der Prozedur gebraucht!}
MOV BP,CX
SHL BP,1
SHL BP,1 {BP := (zu zeichnende Zeilen)*4 }
MOV ES,PAGEADR {(Segment-)Zieladresse ist aktive Grafikseite}
MOV DS,SCROLLADR {(Segment-)Quelladresse ist die Seite SCROLLPAGE}
MOV DX,3C4h
MOV AL,2
AND BX,3 {BX:=Start-Schreibplane (Start-LESEplane=0)}
JNE @mode0b {nur falls x mod 4=0 ist koennen wir WriteMode1 verwenden}
{--- "Abkuerzung" ueber WriteMode 1 moeglich!}
MOV AH,0Fh {alle 4 Planes gleichzeitig bearbeiten}
OUT DX,AX
MOV AX,4105h
MOV DX,3CEh
OUT DX,AX {WriteMode 1 waehlen}
MOV BX,CX {BX:=CX=zu zeichnende Zeilen}
JMP @lastPlane2 {abkuerzen}
{---}
@mode0b:
MOV AH,CS:[OFFSET CS_TranslateTab+BX]
OUT DX,AX
PUSH AX {aktuelle Schreibplane merken}
MOV DX,3CEh
MOV AX,0004h {aktuelle Leseplane 0 waehlen}
OUT DX,AX
MOV AX,LINESIZE-4 {Korrekturfaktor fuer Zeilenadressen}
MOV BX,CX {zu zeichnende Zeilenzahl nach BX retten}
@eineZeile1b:
MOVSB
MOVSB
MOVSB
MOVSB
ADD DI,AX {DI auf naechste Zeile setzen}
LOOP @eineZeile1b
MOV AX,0104h {DX=3CEh -> Leseplane 1 waehlen}
OUT DX,AX
MOV DX,3C4h {naechste Schreibplane:}
POP AX
SHL AH,1
CMP AH,16
JNE @nowrap1b
MOV AH,1 {nach Plane 3 kommt wieder Plane 0, aber}
INC DI {die Zieladresse hat sich um 1 Byte erhoeht}
@nowrap1b:
OUT DX,AX
PUSH AX
SHL BX,1 {DI wieder auf Ursprungsadresse zuruecksetzen:}
SUB DI,CS:[OFFSET GADR + BX] {dazu DI um (zu zeichnende Zeilen)*LINESIZE}
SHR BX,1 {verringern (N.B.: BX=zu zeichnende Zeilen)}
SUB SI,BP {SI auch zuruecksetzen}
MOV AX,LINESIZE-4
MOV CX,BX {CX:=zu zeichnende Zeilenanzahl}
@eineZeile2b:
MOVSB
MOVSB
MOVSB
MOVSB
ADD DI,AX {DI auf naechste Zeile setzen}
LOOP @eineZeile2b
POP AX
SHL AH,1
CMP AH,16
JNE @nowrap2b
MOV AH,1 {nach Plane 3 kommt wieder Plane 0, aber}
INC DI {die Zieladresse hat sich um 1 Byte erhoeht}
@nowrap2b:
OUT DX,AX {DX=3C4h -> Schreibplane setzen}
PUSH AX
MOV DX,3CEh
MOV AX,0204h {Leseplane 2 setzen}
OUT DX,AX
SHL BX,1 {DI wieder auf Ursprungsadresse zuruecksetzen:}
SUB DI,CS:[OFFSET GADR + BX] {dazu DI um (zu zeichnende Zeilen)*LINESIZE}
SHR BX,1 {verringern (N.B.: BX=zu zeichnende Zeilen)}
SUB SI,BP {SI auch zuruecksetzen}
MOV AX,LINESIZE-4
MOV CX,BX {CX:=zu zeichnende Zeilenzahl}
@eineZeile3b:
MOVSB
MOVSB
MOVSB
MOVSB
ADD DI,AX {DI auf naechste Zeile setzen}
LOOP @eineZeile3b
MOV AX,0304h
OUT DX,AX {DX=3CEh -> Leseplane 3 waehlen}
MOV DX,3C4h
POP AX
SHL AH,1
CMP AH,16
JNE @nowrap3b
MOV AH,1 {nach Plane 3 kommt wieder Plane 0, aber}
INC DI {die Zieladresse hat sich um 1 Byte erhoeht}
@nowrap3b:
OUT DX,AX {DX=3C4h -> Schreibplane setzen}
SHL BX,1 {DI wieder auf Ursprungsadresse zuruecksetzen:}
SUB DI,CS:[OFFSET GADR + BX] {dazu DI um (zu zeichnende Zeilen)*LINESIZE}
SHR BX,1 {verringern (N.B.: BX=zu zeichnende Zeilen)}
SUB SI,BP {SI auch zuruecksetzen}
@lastPlane2:
MOV AX,LINESIZE-4
MOV CX,BX {CX:=zu zeichnende Zeilenzahl}
@eineZeile4b:
MOVSB
MOVSB
MOVSB
MOVSB
ADD DI,AX {DI auf naechste Zeile setzen}
LOOP @eineZeile4b
{--- falls Abkuerzung genommen wurde, muss WriteMode 0 wieder}
{ gesetzt werden; hier der Einfachheit immer neugesetzt }
MOV AX,4005h
MOV DX,3CEh
OUT DX,AX
{---}
POP BP
MOV AX,SEG @Data {DS wiederherstellen}
MOV DS,AX
INC xt {Tilezaehler in X-Richtung erhoehen}
INC actIndex {wg. actIndex=yt*XTiles+xt auch actIndex erhoehen}
MOV AX,x {aktuelle X-Koord. auch weitersetzen: x:=x+16}
ADD AX,16
MOV x,AX
CMP AX,XMAX+1-16
JBE @repeat1 {bis rechte obere Ecke erreicht}
{Jetzt obere rechte Eck-Kachel - falls nicht schon gezeichnet:}
CMP AX,XMAX+1
JE @label1
MOV CL,offscreenFlag {ja, Ecke muss noch gezeichnet werden}
XOR SI,SI {Tileindex bestimmen:}
DEC CL {IF offscreenFlag OR (xt<0) OR (xt>=XTiles) }
JZ @go3 { THEN index:=0 ELSE index:=actIndex =yt*XTiles+xt}
MOV DX,xt
OR DX,DX
JS @go3
CMP DX,XTiles
JAE @go3
MOV SI,actIndex
@go3:
{PROCEDURE DrawUpperRightTile(x,rightcut,topcut:INTEGER; index:WORD);}
{ in: (x,0) = linke obere Ecke der zu zeichnenden Tile}
{ rightcut = Anzahl der rechts abzuschneidenden Tile-Spalten}
{ topcut = dto., oben}
{ SI = index = Tilenummer}
{out: Tile wurde auf aktueller Seite PAGEADR gezeichnet}
{rem: Tile wurde rechts und oben entsprechend geschnitten}
{ topcut muss zwischen 0 und 15 liegen, 16 ist verboten}
{ rightcut muss zwischen 0 und 15 liegen, 16 ist erlaubt (aber unsinnig)!}
{ rightcut koennte auch berechnet werden (fuer x>xmax-16) gemaess}
{ rightcut := x+15-xmax }
MOV AH,[OFFSET BackTile +SI] {AH:=BackTile[index]}
XOR AL,AL
SHR AH,1 {AX:=Kachel*64 =Kachel SHL 6 =(Kachel SHL 8) SHR 2}
RCR AL,1 {deshalb: AH:=Kachel und anschliessend AX um 2 Bit}
SHR AH,1 {rechtsschieben!}
RCR AL,1
MOV SI,topcut
MOV CX,16
SUB CX,SI {CX:=16-topcut = Anzahl zu zeichnende Zeilen}
SHL SI,1
SHL SI,1 {fuer jede oben abgeschnittene Zeile 4 Bytes}
ADD SI,AX {zur (Offset-)Quelladresse in Page 3 addieren}
MOV DI,x {erste Zieladresse ist DI:=0*LINESIZE +(x div 4)=x div 4}
MOV BX,DI {Kopie von x nach BX}
SHR DI,1
SHR DI,1
MOV AX,rightcut
{Jetzt wird keine Variable aus dem Stack mehr gebraucht: BP kann}
{verwendet werden!}
PUSH BP {wird beim Verlassen der Prozedur gebraucht!}
MOV BP,16+3
SUB BP,AX {BP:=16+3-rightcut, denn die Zahl der Bytes je Zeile fuer}
{Plane i berechnet sich zu (16+3-i-rightcut) SHR 2 }
MOV AH,AL {rightcut nach AH retten}
MOV ES,PAGEADR {(Segment-)Zieladresse ist aktive Grafikseite}
MOV DS,SCROLLADR {(Segment-)Quelladresse ist die Seite SCROLLPAGE}
MOV DX,3C4h
MOV AL,2
AND BX,3
JNE @mode0c {nur falls x mod 4=0 _und_ rightcut mod 4=0 ist koennen}
AND AH,3 {wir WriteMode1 verwenden!}
JNE @mode0c
{--- "Abkuerzung" ueber WriteMode 1 moeglich!: DX=3C4h, BP=16+3-rightcut}
MOV AH,0Fh
OUT DX,AX {alle 4 Planes gleichzeitig bearbeiten}
MOV DX,3CEh
MOV AX,4105h {WriteMode 1 waehlen}
OUT DX,AX
SUB BP,3 {BP auf die richtige Groesse bringen}
JMP @lastPlane3 {abkuerzen}
{---}
@mode0c:
MOV AH,CS:[OFFSET CS_TranslateTab +BX]
OUT DX,AX {aktuelle Schreibplane waehlen}
PUSH AX {und fuer spaeter merken}
MOV DX,3CEh
MOV AX,0004h {Leseplane 0 waehlen}
OUT DX,AX
MOV BX,BP
SHR BX,1
SHR BX,1 {BX:=Bytes_je_Plane0_Zeile = (16+3-rightcut) DIV 4}
MOV AX,LINESIZE {Korrekturfaktor fuer Zeilenzieladressen}
SUB AX,BX { := LINESIZE-Bytes_je_Plane0_Zeile}
MOV DX,4 {Korrekturfaktor fuer Tilezeilenquelladressen}
SUB DX,BX { := 4-Bytes_je_Plane0_Zeile}
PUSH SI {Quell- und Zieladresse fuer weitere Planes retten}
PUSH DI
PUSH BP {BP retten}
PUSH CX {CX = Zeilenzaehler retten}
MOV BP,CX {BP:=Zeilenzaehler}
@eineZeile1c:
MOV CX,BX {CX:=Bytes_je_Plane0_Zeile}
REP MOVSB {eine Zeile uebertragen}
ADD DI,AX {DI auf naechste Zielzeile setzen}
ADD SI,DX {SI auf naechste Tilequellzeile setzen}
DEC BP
JNZ @eineZeile1c
POP CX
POP BP
POP DI
POP SI
MOV DX,3CEh
MOV AX,0104h {Leseplane 1 waehlen}
OUT DX,AX
MOV DX,3C4h {naechste Schreibplane:}
POP AX
SHL AH,1
CMP AH,16
JNE @nowrap1c
MOV AH,1 {nach Plane 3 kommt wieder Plane 0, aber}
INC DI {die Zieladresse hat sich um 1 Byte erhoeht}
@nowrap1c:
OUT DX,AX
PUSH AX
DEC BP {BP:=16+2-rightcut}
MOV BX,BP
SHR BX,1
SHR BX,1 {BX:=Bytes_je_Plane1_Zeile}
MOV AX,LINESIZE {Korrekturfaktor fuer Zeilenzieladressen}
SUB AX,BX { := LINESIZE-Bytes_je_Plane1_Zeile}
MOV DX,4 {Korrekturfaktor fuer Tilezeilenquelladressen}
SUB DX,BX { := 4-Bytes_je_Plane1_Zeile}
PUSH SI {Quell- und Zieladresse fuer weitere Planes retten}
PUSH DI
PUSH BP {BP retten}
PUSH CX {CX = Zeilenzaehler retten}
MOV BP,CX {BP:=Zeilenzaehler}
@eineZeile2c:
MOV CX,BX {CX:=Bytes_je_Plane0_Zeile}
REP MOVSB {eine Zeile uebertragen}
ADD DI,AX {DI auf naechste Zielzeile setzen}
ADD SI,DX {SI auf naechste Tilequellzeile setzen}
DEC BP
JNZ @eineZeile2c
POP CX
POP BP
POP DI
POP SI
MOV DX,3CEh
MOV AX,0204h {Leseplane 2 waehlen}
OUT DX,AX
MOV DX,3C4h {naechste Schreibplane:}
POP AX
SHL AH,1
CMP AH,16
JNE @nowrap2c
MOV AH,1 {nach Plane 3 kommt wieder Plane 0, aber}
INC DI {die Zieladresse hat sich um 1 Byte erhoeht}
@nowrap2c:
OUT DX,AX
PUSH AX
DEC BP {BP:=16+1-rightcut}
MOV BX,BP
SHR BX,1
SHR BX,1 {BX:=Bytes_je_Plane2_Zeile}
MOV AX,LINESIZE {Korrekturfaktor fuer Zeilenzieladressen}
SUB AX,BX { := LINESIZE-Bytes_je_Plane2_Zeile}
MOV DX,4 {Korrekturfaktor fuer Tilezeilenquelladressen}
SUB DX,BX { := 4-Bytes_je_Plane2_Zeile}
PUSH SI {Quell- und Zieladresse fuer weitere Planes retten}
PUSH DI
PUSH BP {BP retten}
PUSH CX {CX = Zeilenzaehler retten}
MOV BP,CX {BP:=Zeilenzaehler}
@eineZeile3c:
MOV CX,BX {CX:=Bytes_je_Plane0_Zeile}
REP MOVSB {eine Zeile uebertragen}
ADD DI,AX {DI auf naechste Zielzeile setzen}
ADD SI,DX {SI auf naechste Tilequellzeile setzen}
DEC BP
JNZ @eineZeile3c
POP CX
POP BP
POP DI
POP SI
MOV DX,3CEh
MOV AX,0304h {Leseplane 3 waehlen}
OUT DX,AX
MOV DX,3C4h {naechste Schreibplane:}
POP AX
SHL AH,1
CMP AH,16
JNE @nowrap3c
MOV AH,1 {nach Plane 3 kommt wieder Plane 0, aber}
INC DI {die Zieladresse hat sich um 1 Byte erhoeht}
@nowrap3c:
OUT DX,AX
{Wert nicht mehr pushen!}
DEC BP {BP:=16-rightcut}
@lastplane3:
{BP direkt verwenden}
SHR BP,1
SHR BP,1 {BP:=Bytes_je_Plane3_Zeile}
MOV AX,LINESIZE {Korrekturfaktor fuer Zeilenzieladressen}
SUB AX,BP { := LINESIZE-Bytes_je_Plane3_Zeile}
MOV DX,4 {Korrekturfaktor fuer Tilezeilenquelladressen}
SUB DX,BP { := 4-Bytes_je_Plane3_Zeile}
{Quell- und Zieladresse nicht mehr retten}
MOV BX,CX {BX:=Zeilenzaehler}
@eineZeile4c:
MOV CX,BP {CX:=Bytes_je_Plane0_Zeile}
REP MOVSB {eine Zeile uebertragen}
ADD DI,AX {DI auf naechste Zielzeile setzen}
ADD SI,DX {SI auf naechste Tilequellzeile setzen}
DEC BX
JNZ @eineZeile4c
{--- falls Abkuerzung genommen wurde, muss WriteMode 0 wieder}
{ gesetzt werden; hier der Einfachheit immer neugesetzt }
MOV AX,4005h
MOV DX,3CEh
OUT DX,AX
{---}
POP BP
MOV AX,SEG @Data {DS wiederherstellen}
MOV DS,AX
@label1:
MOV AX,actIndex {actIndex fuer naechste Tilezeile justieren:}
STC {actIndex:=actIndex-tiles+1}
SBB AX,tiles
MOV actIndex,AX
{Jetzt alle vollstaendig im Inneren des Bildschirms gelegenen }
{(d.h.: nicht abgeschnittene) Tiles zeichnen:}
@do_innertiles:
MOV AX,ypix
MOV y,AX
MOV AX,actIndex
ADD AX,XTiles
MOV RandIndex,AX
INC AX
MOV actIndex,AX
@repeat2:
MOV AX,ytil
MOV CL,1
OR AX,AX
JS @go4
CMP AX,YTiles
JAE @go4
DEC CL
@go4:
MOV offscreenFlag,CL
MOV AX,xpix
MOV x,AX
MOV AX,offsetXTiles
MOV xtil,AX
@repeat3:
MOV CL,offscreenFlag
XOR SI,SI
DEC CL
JZ @go5
OR AX,AX
JS @go5
CMP AX,XTiles
JAE @go5
MOV SI,actIndex
@go5:
{PROCEDURE DrawInnerTile(x,y:INTEGER; index:WORD);}
{ in: (x,y) = linke obere Ecke der zu zeichnenden Tile,}
{ SI = index = Tilenummer}
{out: Tile wurde auf aktueller Seite PAGEADR gezeichnet}
{rem: Tile muss ganz auf dem Bildschirm sein, es findet}
{ kein Clipping statt}
MOV AH,[OFFSET BackTile +SI] {AH:=BackTile[index]}
XOR AL,AL
SHR AH,1 {AX:=Kachel*64 =Kachel SHL 6 =(Kachel SHL 8) SHR 2}
RCR AL,1 {deshalb: AH:=Kachel und anschliessend AX um 2 Bit}
SHR AH,1 {rechtsschieben!}
RCR AL,1
MOV SI,AX {Das ist zugleich die (Offset-)Quelladresse in Page 3}
MOV DI,y {die erste Zieladresse ist DI:=y*LINESIZE+(x div 4)}
SHL DI,1
MOV DI,CS:[OFFSET GADR + DI]
MOV AX,x
MOV BX,AX {Kopie von X in BX aufbewahren}
SHR AX,1
SHR AX,1
ADD DI,AX
MOV ES,PAGEADR {(Segment-)Zieladresse ist aktive Grafikseite}
MOV DS,SCROLLADR {(Segment-)Quelladresse ist die Seite SCROLLPAGE}
MOV DX,3C4h
MOV AL,2
AND BX,3 {BX:=Start-Schreibplane (Start-LESEplane=0)}
JNE @mode0d {nur falls x mod 4=0 ist koennen wir WriteMode1 verwenden}
{--- "Abkuerzung" ueber WriteMode 1 moeglich!}
MOV AH,0Fh {alle 4 Planes gleichzeitig bearbeiten}
OUT DX,AX
MOV AX,4105h
MOV DX,3CEh
OUT DX,AX {WriteMode 1 waehlen}
MOV BX,4
JMP @lastPlane4 {abkuerzen}
{---}
@mode0d:
MOV AH,CS:[OFFSET CS_TranslateTab+BX]
OUT DX,AX
PUSH AX {aktuelle Schreibplane merken}
MOV DX,3CEh
MOV AX,0004h {aktuelle Leseplane 0 waehlen}
OUT DX,AX
MOV BX,AX {16 horizontale Punkte = 4 Bytes pro Zeile: BX:=4}
{(nutzt aus, dass AX zufaelligerweise gerade 4 ist)}
MOV AX,LINESIZE-4 {Korrekturfaktor fuer Zeilenadressen}
MOV CX,BX {CX:=4}
REP MOVSB {1.Zeile zeichnen}
ADD DI,AX {DI auf naechste Zeile setzen}
MOV CX,BX
REP MOVSB {2.Zeile}
ADD DI,AX
MOV CX,BX
REP MOVSB {3.Zeile}
ADD DI,AX
MOV CX,BX
REP MOVSB {4.Zeile}
ADD DI,AX
MOV CX,BX
REP MOVSB {5.Zeile}
ADD DI,AX
MOV CX,BX
REP MOVSB {6.Zeile}
ADD DI,AX
MOV CX,BX
REP MOVSB {7.Zeile}
ADD DI,AX
MOV CX,BX
REP MOVSB {8.Zeile}
ADD DI,AX
MOV CX,BX
REP MOVSB {9.Zeile}
ADD DI,AX
MOV CX,BX
REP MOVSB {10.Zeile}
ADD DI,AX
MOV CX,BX
REP MOVSB {11.Zeile}
ADD DI,AX
MOV CX,BX
REP MOVSB {12.Zeile}
ADD DI,AX
MOV CX,BX
REP MOVSB {13.Zeile}
ADD DI,AX
MOV CX,BX
REP MOVSB {14.Zeile}
ADD DI,AX
MOV CX,BX
REP MOVSB {15.Zeile}
ADD DI,AX
MOV CX,BX
REP MOVSB {16.Zeile}
ADD DI,AX
MOV AX,0104h {DX=3CEh -> Leseplane 1 waehlen}
OUT DX,AX
MOV DX,3C4h {naechste Schreibplane:}
POP AX
SHL AH,1
CMP AH,16
JNE @nowrap1d
MOV AH,1 {nach Plane 3 kommt wieder Plane 0, aber}
INC DI {die Zieladresse hat sich um 1 Byte erhoeht}
@nowrap1d:
OUT DX,AX
PUSH AX
SUB DI,16*LINESIZE {DI wieder auf Ursprungsadresse zuruecksetzen}
SUB SI,16*4 {SI auch}
MOV AX,LINESIZE-4
MOV CX,BX {CX:=4}
REP MOVSB {1.Zeile zeichnen}
ADD DI,AX {DI auf naechste Zeile setzen}
MOV CX,BX
REP MOVSB {2.Zeile}
ADD DI,AX
MOV CX,BX
REP MOVSB {3.Zeile}
ADD DI,AX
MOV CX,BX
REP MOVSB {4.Zeile}
ADD DI,AX
MOV CX,BX
REP MOVSB {5.Zeile}
ADD DI,AX
MOV CX,BX
REP MOVSB {6.Zeile}
ADD DI,AX
MOV CX,BX
REP MOVSB {7.Zeile}
ADD DI,AX
MOV CX,BX
REP MOVSB {8.Zeile}
ADD DI,AX
MOV CX,BX
REP MOVSB {9.Zeile}
ADD DI,AX
MOV CX,BX
REP MOVSB {10.Zeile}
ADD DI,AX
MOV CX,BX
REP MOVSB {11.Zeile}
ADD DI,AX
MOV CX,BX
REP MOVSB {12.Zeile}
ADD DI,AX
MOV CX,BX
REP MOVSB {13.Zeile}
ADD DI,AX
MOV CX,BX
REP MOVSB {14.Zeile}
ADD DI,AX
MOV CX,BX
REP MOVSB {15.Zeile}
ADD DI,AX
MOV CX,BX
REP MOVSB {16.Zeile}
ADD DI,AX
POP AX
SHL AH,1
CMP AH,16
JNE @nowrap2d
MOV AH,1 {nach Plane 3 kommt wieder Plane 0, aber}
INC DI {die Zieladresse hat sich um 1 Byte erhoeht}
@nowrap2d:
OUT DX,AX {DX=3C4h -> Schreibplane setzen}
PUSH AX
MOV DX,3CEh
MOV AX,0204h {Leseplane 2 setzen}
OUT DX,AX
SUB DI,16*LINESIZE
SUB SI,16*4
MOV AX,LINESIZE-4
MOV CX,BX {CX:=4}
REP MOVSB {1.Zeile zeichnen}
ADD DI,AX {DI auf naechste Zeile setzen}
MOV CX,BX
REP MOVSB {2.Zeile}
ADD DI,AX
MOV CX,BX
REP MOVSB {3.Zeile}
ADD DI,AX
MOV CX,BX
REP MOVSB {4.Zeile}
ADD DI,AX
MOV CX,BX
REP MOVSB {5.Zeile}
ADD DI,AX
MOV CX,BX
REP MOVSB {6.Zeile}
ADD DI,AX
MOV CX,BX
REP MOVSB {7.Zeile}
ADD DI,AX
MOV CX,BX
REP MOVSB {8.Zeile}
ADD DI,AX
MOV CX,BX
REP MOVSB {9.Zeile}
ADD DI,AX
MOV CX,BX
REP MOVSB {10.Zeile}
ADD DI,AX
MOV CX,BX
REP MOVSB {11.Zeile}
ADD DI,AX
MOV CX,BX
REP MOVSB {12.Zeile}
ADD DI,AX
MOV CX,BX
REP MOVSB {13.Zeile}
ADD DI,AX
MOV CX,BX
REP MOVSB {14.Zeile}
ADD DI,AX
MOV CX,BX
REP MOVSB {15.Zeile}
ADD DI,AX
MOV CX,BX
REP MOVSB {16.Zeile}
ADD DI,AX
MOV AX,0304h
OUT DX,AX {DX=3CEh -> Leseplane 3 waehlen}
MOV DX,3C4h
POP AX
SHL AH,1
CMP AH,16
JNE @nowrap3d
MOV AH,1 {nach Plane 3 kommt wieder Plane 0, aber}
INC DI {die Zieladresse hat sich um 1 Byte erhoeht}
@nowrap3d:
OUT DX,AX {DX=3C4h -> Schreibplane setzen}
SUB DI,16*LINESIZE
SUB SI,16*4
@lastPlane4:
MOV AX,LINESIZE-4
MOV CX,BX {CX:=4}
REP MOVSB {1.Zeile zeichnen}
ADD DI,AX {DI auf naechste Zeile setzen}
MOV CX,BX
REP MOVSB {2.Zeile}
ADD DI,AX
MOV CX,BX
REP MOVSB {3.Zeile}
ADD DI,AX
MOV CX,BX
REP MOVSB {4.Zeile}
ADD DI,AX
MOV CX,BX
REP MOVSB {5.Zeile}
ADD DI,AX
MOV CX,BX
REP MOVSB {6.Zeile}
ADD DI,AX
MOV CX,BX
REP MOVSB {7.Zeile}
ADD DI,AX
MOV CX,BX
REP MOVSB {8.Zeile}
ADD DI,AX
MOV CX,BX
REP MOVSB {9.Zeile}
ADD DI,AX
MOV CX,BX
REP MOVSB {10.Zeile}
ADD DI,AX
MOV CX,BX
REP MOVSB {11.Zeile}
ADD DI,AX
MOV CX,BX
REP MOVSB {12.Zeile}
ADD DI,AX
MOV CX,BX
REP MOVSB {13.Zeile}
ADD DI,AX
MOV CX,BX
REP MOVSB {14.Zeile}
ADD DI,AX
MOV CX,BX
REP MOVSB {15.Zeile}
ADD DI,AX
MOV CX,BX
REP MOVSB {16.Zeile}
{--- falls Abkuerzung genommen wurde, muss WriteMode 0 wieder}
{ gesetzt werden; hier der Einfachheit immer neugesetzt }
MOV AX,4005h
MOV DX,3CEh
OUT DX,AX
{---}
MOV AX,SEG @Data {DS wiederherstellen}
MOV DS,AX
INC actIndex
MOV AX,xtil
INC AX
MOV xtil,AX
MOV DX,x
ADD DX,16
MOV x,DX
CMP DX,XMAX+1-16
JBE @repeat3
INC ytil
MOV AX,actIndex
SUB AX,tiles
ADD AX,XTiles
MOV actIndex,AX
MOV AX,y
ADD AX,16
MOV y,AX
CMP AX,YMAX+1-16
JBE @repeat2
{Jetzt die untere rechte Tile - falls sie nicht bereits schon gezeichnet}
{wurde: }
CMP bottomcut,0
JE @do_raender
MOV DX,offsetXTiles
MOV xt,DX
MOV AX,ytil
MOV yt,AX
MOV CL,1
OR AX,AX
JS @go6
CMP AX,YTiles
JAE @go6
DEC CL
@go6:
MOV offscreenFlag,CL
CMP leftcut,0
JE @label2
XOR SI,SI
DEC CL
JZ @go7
DEC DX {DX=xt-1}
JS @go7
CMP DX,XTiles
JAE @go7
MOV SI,actIndex
DEC SI
@go7:
{PROCEDURE DrawLowerLeftTile(y,leftcut,bottomcut:INTEGER; index:WORD);}
{ in: (0,y) = linke obere Ecke der zu zeichnenden Tile}
{ leftcut = Anzahl der links abzuschneidenden Tile-Spalten}
{ bottomcut = dto., unten}
{ SI = index = Tilenummer}
{out: Tile wurde auf aktueller Seite PAGEADR gezeichnet}
{rem: Tile wurde links und unten entsprechend geschnitten}
{ leftcut muss zwischen 0 und 15 liegen, 16 ist erlaubt (aber unsinnig)}
{ bottomcut muss zwischen 0 und 15 liegen, 16 ist verboten}
{ bottomcut koennte auch ausgerechnet werden ueber:}
{ bottomcut:=y+15-ymax}
MOV AH,[OFFSET BackTile +SI] {AH:=BackTile[index]}
XOR AL,AL
SHR AH,1 {AX:=Kachel*64 =Kachel SHL 6 =(Kachel SHL 8) SHR 2}
RCR AL,1 {deshalb: AH:=Kachel und anschliessend AX um 2 Bit}
SHR AH,1 {rechtsschieben!}
RCR AL,1
MOV SI,AX {Das ist zugleich die (Offset-)Quelladresse in Page 3}
MOV DI,y {erste Zieladresse ist DI:=y*LINESIZE+(0 div 4)=y*LINESIZE}
SHL DI,1
MOV DI,CS:[OFFSET GADR + DI]
MOV AX,leftcut
MOV BX,AX {Kopie von leftcut in BX aufbewahren}
SHR AX,1
SHR AX,1
ADD SI,AX {SI um cutoff in Bytes weitersetzen=leftcut div 4}
MOV CX,16
SUB CX,bottomcut {CX:=16-bottomcut = zu zeichnende Zeilen}
{Jetzt wird keine Variable aus dem Stack mehr gebraucht: BP kann}
{verwendet werden!}
PUSH BP {wird beim Verlassen der Prozedur gebraucht!}
MOV BP,16+3
SUB BP,BX {BP:=16+3-leftcut, denn die Zahl der Bytes je Zeile fuer}
{Plane i berechnet sich zu (16+3-i-leftcut) SHR 2 }
MOV ES,PAGEADR {(Segment-)Zieladresse ist aktive Grafikseite}
MOV DS,SCROLLADR {(Segment-)Quelladresse ist die Seite SCROLLPAGE}
MOV DX,3CEh
AND BL,3
MOV AH,BL {AH:=leftcut mod 4}
MOV AL,4
JNE @mode0e {nur falls leftcut mod 4=0 ist koennen wir WriteMode1 verwenden}
{--- "Abkuerzung" ueber WriteMode 1 moeglich!: DX=3CEh, BP=16+3-leftcut}
{ Dass BP "3 zu gross ist" stoert nicht, da dieser Code nur ausge- }
{ fuehrt wird, wenn leftcut mod 4=0, also 16+3-leftcut=...11b, die }
{ "11b" werden also beim Rechtsschieben eh abgeschnitten!}
MOV AX,4105h
OUT DX,AX {WriteMode 1 waehlen}
MOV DX,3C4h
MOV AX,0F02h {alle 4 Planes gleichzeitig bearbeiten}
OUT DX,AX
JMP @lastPlane5 {abkuerzen}
{---}
@mode0e:
OUT DX,AX {aktuelle Leseplane waehlen}
PUSH AX {und fuer spaeter merken}
MOV DX,3C4h
MOV AX,0102h {Schreibplane 0 waehlen}
OUT DX,AX
MOV BX,BP
SHR BX,1
SHR BX,1 {BX:=Bytes_je_Plane0_Zeile = (16+3-leftcut) DIV 4}
MOV AX,LINESIZE {Korrekturfaktor fuer Zeilenzieladressen}
SUB AX,BX { := LINESIZE-Bytes_je_Plane0_Zeile}
MOV DX,4 {Korrekturfaktor fuer Tilezeilenquelladressen}
SUB DX,BX { := 4-Bytes_je_Plane0_Zeile}
PUSH SI {Quell- und Zieladresse fuer weitere Planes retten}
PUSH DI
PUSH BP {BP retten}
PUSH CX {CX = Zeilenzaehler retten}
MOV BP,CX {BP:=Zeilenzaehler}
@eineZeile1d:
MOV CX,BX {CX:=Bytes_je_Plane0_Zeile}
REP MOVSB {eine Zeile uebertragen}
ADD DI,AX {DI auf naechste Zielzeile setzen}
ADD SI,DX {SI auf naechste Tilequellzeile setzen}
DEC BP
JNZ @eineZeile1d
POP CX
POP BP
POP DI
POP SI
MOV DX,3C4h
MOV AX,0202h {Schreibplane 1 waehlen}
OUT DX,AX
MOV DX,3CEh {naechste Leseplane:}
POP AX
INC AH
AND AH,3 {um 1 erhoehen MOD 4}
JNE @nowrap1e
INC SI {nach Plane 3 kommt wieder Plane 0, aber}
{die Quelladresse hat sich um 1 Byte erhoeht}
@nowrap1e:
OUT DX,AX
PUSH AX
DEC BP {BP:=16+2-leftcut}
MOV BX,BP
SHR BX,1
SHR BX,1 {BX:=Bytes_je_Plane1_Zeile}
MOV AX,LINESIZE {Korrekturfaktor fuer Zeilenzieladressen}
SUB AX,BX { := LINESIZE-Bytes_je_Plane1_Zeile}
MOV DX,4 {Korrekturfaktor fuer Tilezeilenquelladressen}
SUB DX,BX { := 4-Bytes_je_Plane1_Zeile}
PUSH SI {Quell- und Zieladresse fuer weitere Planes retten}
PUSH DI
PUSH BP {BP retten}
PUSH CX {CX = Zeilenzaehler retten}
MOV BP,CX {BP:=Zeilenzaehler}
@eineZeile2d:
MOV CX,BX {CX:=Bytes_je_Plane0_Zeile}
REP MOVSB {eine Zeile uebertragen}
ADD DI,AX {DI auf naechste Zielzeile setzen}
ADD SI,DX {SI auf naechste Tilequellzeile setzen}
DEC BP
JNZ @eineZeile2d
POP CX
POP BP
POP DI
POP SI
MOV DX,3C4h
MOV AX,0402h {Schreibplane 2 waehlen}
OUT DX,AX
MOV DX,3CEh {naechste Leseplane:}
POP AX
INC AH
AND AH,3 {um 1 erhoehen MOD 4}
JNE @nowrap2e
INC SI {nach Plane 3 kommt wieder Plane 0, aber}
{die Quelladresse hat sich um 1 Byte erhoeht}
@nowrap2e:
OUT DX,AX
PUSH AX
DEC BP {BP:=16+1-leftcut}
MOV BX,BP
SHR BX,1
SHR BX,1 {BX:=Bytes_je_Plane2_Zeile}
MOV AX,LINESIZE {Korrekturfaktor fuer Zeilenzieladressen}
SUB AX,BX { := LINESIZE-Bytes_je_Plane2_Zeile}
MOV DX,4 {Korrekturfaktor fuer Tilezeilenquelladressen}
SUB DX,BX { := 4-Bytes_je_Plane2_Zeile}
PUSH SI {Quell- und Zieladresse fuer weitere Planes retten}
PUSH DI
PUSH BP {BP retten}
PUSH CX {CX = Zeilenzaehler retten}
MOV BP,CX {BP:=Zeilenzaehler}
@eineZeile3d:
MOV CX,BX {CX:=Bytes_je_Plane0_Zeile}
REP MOVSB {eine Zeile uebertragen}
ADD DI,AX {DI auf naechste Zielzeile setzen}
ADD SI,DX {SI auf naechste Tilequellzeile setzen}
DEC BP
JNZ @eineZeile3d
POP CX
POP BP
POP DI
POP SI
MOV DX,3C4h
MOV AX,0802h {Schreibplane 3 waehlen}
OUT DX,AX
MOV DX,3CEh {naechste Leseplane:}
POP AX
INC AH
AND AH,3 {um 1 erhoehen MOD 4}
JNE @nowrap3e
INC SI {nach Plane 3 kommt wieder Plane 0, aber}
{die Quelladresse hat sich um 1 Byte erhoeht}
@nowrap3e:
OUT DX,AX
{Wert nicht mehr pushen!}
DEC BP {BP:=16-leftcut}
@lastplane5:
{BP direkt verwenden}
SHR BP,1
SHR BP,1 {BP:=Bytes_je_Plane3_Zeile}
MOV AX,LINESIZE {Korrekturfaktor fuer Zeilenzieladressen}
SUB AX,BP { := LINESIZE-Bytes_je_Plane3_Zeile}
MOV DX,4 {Korrekturfaktor fuer Tilezeilenquelladressen}
SUB DX,BP { := 4-Bytes_je_Plane3_Zeile}
MOV BX,CX {BX:=Zeilenzaehler}
@eineZeile4d:
MOV CX,BP {CX:=Bytes_je_Plane0_Zeile}
REP MOVSB {eine Zeile uebertragen}
ADD DI,AX {DI auf naechste Zielzeile setzen}
ADD SI,DX {SI auf naechste Tilequellzeile setzen}
DEC BX
JNZ @eineZeile4d
{--- falls Abkuerzung genommen wurde, muss WriteMode 0 wieder}
{ gesetzt werden; hier der Einfachheit immer neugesetzt }
MOV AX,4005h
MOV DX,3CEh
OUT DX,AX
{---}
POP BP
MOV AX,SEG @Data {DS wiederherstellen}
MOV DS,AX
@label2:
MOV AX,xpix
MOV x,AX
{Jetzt die unteren Tiles, die nicht seitlich abgeschnitten sind:}
@repeat4:
MOV CL,offscreenFlag
XOR SI,SI
DEC CL
JZ @go8
MOV AX,xt
OR AX,AX
JS @go8
CMP AX,XTiles
JAE @go8
MOV SI,actIndex
@go8:
{PROCEDURE DrawLowerTile(x,y,bottomcut:INTEGER; index:WORD);}
{ in: (x,y) = linke obere Ecke der zu zeichnenden Tile}
{ bottomcut = Anzahl der unten abzuschneidenden Tile-Zeilen}
{ SI = index = Tilenummer}
{out: Tile wurde auf aktueller Seite PAGEADR gezeichnet}
{rem: Tile wurde unten entsprechend geschnitten}
{ bottomcut muss zwischen 0 und 15 liegen, 16 ist verboten!}
{ bottomcut koennte auch ausgerechnet werden ueber:}
{ bottomcut:=y+15-ymax}
MOV AH,[OFFSET BackTile +SI] {AH:=BackTile[index]}
XOR AL,AL
SHR AH,1 {AX:=Kachel*64 =Kachel SHL 6 =(Kachel SHL 8) SHR 2}
RCR AL,1 {deshalb: AH:=Kachel und anschliessend AX um 2 Bit}
SHR AH,1 {rechtsschieben!}
RCR AL,1
MOV SI,AX {Das ist zugleich die (Offset-)Quelladresse in Page 3}
MOV DI,y {die erste Zieladresse ist DI:=y*LINESIZE+(x div 4)}
SHL DI,1
MOV DI,CS:[OFFSET GADR + DI]
MOV AX,x
MOV BX,AX {Kopie von X in BX aufbewahren}
SHR AX,1
SHR AX,1
ADD DI,AX
MOV CX,16
SUB CX,bottomcut {CX:=zu zeichnende Zeilenzahl = 16-bottomcut}
{Jetzt wird keine Variable aus dem Stack mehr gebraucht: BP kann}
{verwendet werden!}
PUSH BP {wird beim Verlassen der Prozedur gebraucht!}
MOV BP,CX
SHL BP,1
SHL BP,1 {BP := (zu zeichnende Zeilen)*4 }
MOV ES,PAGEADR {(Segment-)Zieladresse ist aktive Grafikseite}
MOV DS,SCROLLADR {(Segment-)Quelladresse ist die Seite SCROLLPAGE}
MOV DX,3C4h
MOV AL,2
AND BX,3 {BX:=Start-Schreibplane (Start-LESEplane=0)}
JNE @mode0f {nur falls x mod 4=0 ist koennen wir WriteMode1 verwenden}
{--- "Abkuerzung" ueber WriteMode 1 moeglich!}
MOV AH,0Fh {alle 4 Planes gleichzeitig bearbeiten}
OUT DX,AX
MOV AX,4105h
MOV DX,3CEh
OUT DX,AX {WriteMode 1 waehlen}
MOV BX,CX {BX:=CX=zu zeichnende Zeilen}
JMP @lastPlane6 {abkuerzen}
{---}
@mode0f:
MOV AH,CS:[OFFSET CS_TranslateTab+BX]
OUT DX,AX
PUSH AX {aktuelle Schreibplane merken}
MOV DX,3CEh
MOV AX,0004h {aktuelle Leseplane 0 waehlen}
OUT DX,AX
MOV AX,LINESIZE-4 {Korrekturfaktor fuer Zeilenadressen}
MOV BX,CX {zu zeichnende Zeilenzahl nach BX retten}
@eineZeile1e:
MOVSB
MOVSB
MOVSB
MOVSB
ADD DI,AX {DI auf naechste Zeile setzen}
LOOP @eineZeile1e
MOV AX,0104h {DX=3CEh -> Leseplane 1 waehlen}
OUT DX,AX
MOV DX,3C4h {naechste Schreibplane:}
POP AX
SHL AH,1
CMP AH,16
JNE @nowrap1f
MOV AH,1 {nach Plane 3 kommt wieder Plane 0, aber}
INC DI {die Zieladresse hat sich um 1 Byte erhoeht}
@nowrap1f:
OUT DX,AX
PUSH AX
SHL BX,1 {DI wieder auf Ursprungsadresse zuruecksetzen:}
SUB DI,CS:[OFFSET GADR + BX] {dazu DI um (zu zeichnende Zeilen)*LINESIZE}
SHR BX,1 {verringern (N.B.: BX=zu zeichnende Zeilen)}
SUB SI,BP {SI auch zuruecksetzen}
MOV AX,LINESIZE-4
MOV CX,BX {CX:=zu zeichnende Zeilenanzahl}
@eineZeile2e:
MOVSB
MOVSB
MOVSB
MOVSB
ADD DI,AX {DI auf naechste Zeile setzen}
LOOP @eineZeile2e
POP AX
SHL AH,1
CMP AH,16
JNE @nowrap2f
MOV AH,1 {nach Plane 3 kommt wieder Plane 0, aber}
INC DI {die Zieladresse hat sich um 1 Byte erhoeht}
@nowrap2f:
OUT DX,AX {DX=3C4h -> Schreibplane setzen}
PUSH AX
MOV DX,3CEh
MOV AX,0204h {Leseplane 2 setzen}
OUT DX,AX
SHL BX,1 {DI wieder auf Ursprungsadresse zuruecksetzen:}
SUB DI,CS:[OFFSET GADR + BX] {dazu DI um (zu zeichnende Zeilen)*LINESIZE}
SHR BX,1 {verringern (N.B.: BX=zu zeichnende Zeilen)}
SUB SI,BP {SI auch zuruecksetzen}
MOV AX,LINESIZE-4
MOV CX,BX {CX:=zu zeichnende Zeilenzahl}
@eineZeile3e:
MOVSB
MOVSB
MOVSB
MOVSB
ADD DI,AX {DI auf naechste Zeile setzen}
LOOP @eineZeile3e
MOV AX,0304h
OUT DX,AX {DX=3CEh -> Leseplane 3 waehlen}
MOV DX,3C4h
POP AX
SHL AH,1
CMP AH,16
JNE @nowrap3f
MOV AH,1 {nach Plane 3 kommt wieder Plane 0, aber}
INC DI {die Zieladresse hat sich um 1 Byte erhoeht}
@nowrap3f:
OUT DX,AX {DX=3C4h -> Schreibplane setzen}
SHL BX,1 {DI wieder auf Ursprungsadresse zuruecksetzen:}
SUB DI,CS:[OFFSET GADR + BX] {dazu DI um (zu zeichnende Zeilen)*LINESIZE}
SHR BX,1 {verringern (N.B.: BX=zu zeichnende Zeilen)}
SUB SI,BP {SI auch zuruecksetzen}
@lastPlane6:
MOV AX,LINESIZE-4
MOV CX,BX {CX:=zu zeichnende Zeilenzahl}
@eineZeile4e:
MOVSB
MOVSB
MOVSB
MOVSB
ADD DI,AX {DI auf naechste Zeile setzen}
LOOP @eineZeile4e
{--- falls Abkuerzung genommen wurde, muss WriteMode 0 wieder}
{ gesetzt werden; hier der Einfachheit immer neugesetzt }
MOV AX,4005h
MOV DX,3CEh
OUT DX,AX
{---}
POP BP
MOV AX,SEG @Data {DS wiederherstellen}
MOV DS,AX
INC xt
INC actIndex
MOV AX,x
ADD AX,16
MOV x,AX
CMP AX,XMAX+1-16
JBE @repeat4
{Jetzt evtl. noch die untere rechte Eck-Tile:}
CMP AX,XMAX+1
JE @do_raender
MOV CL,offscreenFlag
XOR SI,SI
DEC CL
JZ @go9
MOV AX,xt
OR AX,AX
JS @go9
CMP AX,XTiles
JAE @go9
MOV SI,actIndex
@go9:
{PROCEDURE DrawLowerRightTile(x,y,rightcut,bottomcut:INTEGER; index:WORD);}
{ in: (x,y) = linke obere Ecke der zu zeichnenden Tile}
{ rightcut = Anzahl der rechts abzuschneidenden Tile-Spalten}
{ bottomcut = dto., unten}
{ bottomcut muss zwischen 0 und 15 liegen, 16 ist verboten}
{ bottomcut koennte auch ausgerechnet werden ueber:}
{ bottomcut:=y+15-ymax}
{ SI = index = Tilenummer}
{out: Tile wurde auf aktueller Seite PAGEADR gezeichnet}
{rem: Tile wurde rechts und unten entsprechend geschnitten}
{ rightcut muss zwischen 0 und 15 liegen, 16 ist erlaubt (aber unsinnig)!}
{ rightcut koennte auch berechnet werden (fuer x>xmax-16) gemaess}
{ rightcut := x+15-xmax }
{ bottomcut muss zwischen 0 und 15 liegen, 16 ist verboten}
{ bottomcut koennte auch ausgerechnet werden ueber:}
{ bottomcut:=y+15-ymax}
MOV AH,[OFFSET BackTile +SI] {AH:=BackTile[index]}
XOR AL,AL
SHR AH,1 {AX:=Kachel*64 =Kachel SHL 6 =(Kachel SHL 8) SHR 2}
RCR AL,1 {deshalb: AH:=Kachel und anschliessend AX um 2 Bit}
SHR AH,1 {rechtsschieben!}
RCR AL,1
MOV SI,AX {Das ist zugleich die (Offset-)Quelladresse in Page 3}
MOV DI,y {die erste Zieladresse ist DI:=y*LINESIZE+(x div 4)}
SHL DI,1
MOV DI,CS:[OFFSET GADR + DI]
MOV AX,x
MOV BX,AX {Kopie von x nach BX}
SHR AX,1
SHR AX,1
ADD DI,AX
MOV AX,rightcut
MOV CX,16
SUB CX,bottomcut {CX:=16-bottomcut = zu zeichnende Zeilen}
{Jetzt wird keine Variable aus dem Stack mehr gebraucht: BP kann}
{verwendet werden!}
PUSH BP {wird beim Verlassen der Prozedur gebraucht!}
MOV BP,16+3
SUB BP,AX {BP:=16+3-rightcut, denn die Zahl der Bytes je Zeile fuer}
{Plane i berechnet sich zu (16+3-i-rightcut) SHR 2 }
MOV AH,AL {rightcut nach AH retten}
MOV ES,PAGEADR {(Segment-)Zieladresse ist aktive Grafikseite}
MOV DS,SCROLLADR {(Segment-)Quelladresse ist die Seite SCROLLPAGE}
MOV DX,3C4h
MOV AL,2
AND BX,3
JNE @mode0g {nur falls x mod 4=0 _und_ rightcut mod 4=0 ist koennen}
AND AH,3 {wir WriteMode1 verwenden!}
JNE @mode0g
{--- "Abkuerzung" ueber WriteMode 1 moeglich!: DX=3C4h, BP=16+3-rightcut}
MOV AH,0Fh
OUT DX,AX {alle 4 Planes gleichzeitig bearbeiten}
MOV DX,3CEh
MOV AX,4105h {WriteMode 1 waehlen}
OUT DX,AX
SUB BP,3 {BP auf die richtige Groesse bringen}
JMP @lastPlane7 {abkuerzen}
{---}
@mode0g:
MOV AH,CS:[OFFSET CS_TranslateTab +BX]
OUT DX,AX {aktuelle Schreibplane waehlen}
PUSH AX {und fuer spaeter merken}
MOV DX,3CEh
MOV AX,0004h {Leseplane 0 waehlen}
OUT DX,AX
MOV BX,BP
SHR BX,1
SHR BX,1 {BX:=Bytes_je_Plane0_Zeile = (16+3-rightcut) DIV 4}
MOV AX,LINESIZE {Korrekturfaktor fuer Zeilenzieladressen}
SUB AX,BX { := LINESIZE-Bytes_je_Plane0_Zeile}
MOV DX,4 {Korrekturfaktor fuer Tilezeilenquelladressen}
SUB DX,BX { := 4-Bytes_je_Plane0_Zeile}
PUSH SI {Quell- und Zieladresse fuer weitere Planes retten}
PUSH DI
PUSH BP {BP retten}
PUSH CX {CX = Zeilenzaehler retten}
MOV BP,CX {BP:=Zeilenzaehler}
@eineZeile1f:
MOV CX,BX {CX:=Bytes_je_Plane0_Zeile}
REP MOVSB {eine Zeile uebertragen}
ADD DI,AX {DI auf naechste Zielzeile setzen}
ADD SI,DX {SI auf naechste Tilequellzeile setzen}
DEC BP
JNZ @eineZeile1f
POP CX
POP BP
POP DI
POP SI
MOV DX,3CEh
MOV AX,0104h {Leseplane 1 waehlen}
OUT DX,AX
MOV DX,3C4h {naechste Schreibplane:}
POP AX
SHL AH,1
CMP AH,16
JNE @nowrap1g
MOV AH,1 {nach Plane 3 kommt wieder Plane 0, aber}
INC DI {die Zieladresse hat sich um 1 Byte erhoeht}
@nowrap1g:
OUT DX,AX
PUSH AX
DEC BP {BP:=16+2-rightcut}
MOV BX,BP
SHR BX,1
SHR BX,1 {BX:=Bytes_je_Plane1_Zeile}
MOV AX,LINESIZE {Korrekturfaktor fuer Zeilenzieladressen}
SUB AX,BX { := LINESIZE-Bytes_je_Plane1_Zeile}
MOV DX,4 {Korrekturfaktor fuer Tilezeilenquelladressen}
SUB DX,BX { := 4-Bytes_je_Plane1_Zeile}
PUSH SI {Quell- und Zieladresse fuer weitere Planes retten}
PUSH DI
PUSH BP {BP retten}
PUSH CX {CX = Zeilenzaehler retten}
MOV BP,CX {BP:=Zeilenzaehler}
@eineZeile2f:
MOV CX,BX {CX:=Bytes_je_Plane0_Zeile}
REP MOVSB {eine Zeile uebertragen}
ADD DI,AX {DI auf naechste Zielzeile setzen}
ADD SI,DX {SI auf naechste Tilequellzeile setzen}
DEC BP
JNZ @eineZeile2f
POP CX
POP BP
POP DI
POP SI
MOV DX,3CEh
MOV AX,0204h {Leseplane 2 waehlen}
OUT DX,AX
MOV DX,3C4h {naechste Schreibplane:}
POP AX
SHL AH,1
CMP AH,16
JNE @nowrap2g
MOV AH,1 {nach Plane 3 kommt wieder Plane 0, aber}
INC DI {die Zieladresse hat sich um 1 Byte erhoeht}
@nowrap2g:
OUT DX,AX
PUSH AX
DEC BP {BP:=16+1-rightcut}
MOV BX,BP
SHR BX,1
SHR BX,1 {BX:=Bytes_je_Plane2_Zeile}
MOV AX,LINESIZE {Korrekturfaktor fuer Zeilenzieladressen}
SUB AX,BX { := LINESIZE-Bytes_je_Plane2_Zeile}
MOV DX,4 {Korrekturfaktor fuer Tilezeilenquelladressen}
SUB DX,BX { := 4-Bytes_je_Plane2_Zeile}
PUSH SI {Quell- und Zieladresse fuer weitere Planes retten}
PUSH DI
PUSH BP {BP retten}
PUSH CX {CX = Zeilenzaehler retten}
MOV BP,CX {BP:=Zeilenzaehler}
@eineZeile3f:
MOV CX,BX {CX:=Bytes_je_Plane0_Zeile}
REP MOVSB {eine Zeile uebertragen}
ADD DI,AX {DI auf naechste Zielzeile setzen}
ADD SI,DX {SI auf naechste Tilequellzeile setzen}
DEC BP
JNZ @eineZeile3f
POP CX
POP BP
POP DI
POP SI
MOV DX,3CEh
MOV AX,0304h {Leseplane 3 waehlen}
OUT DX,AX
MOV DX,3C4h {naechste Schreibplane:}
POP AX
SHL AH,1
CMP AH,16
JNE @nowrap3g
MOV AH,1 {nach Plane 3 kommt wieder Plane 0, aber}
INC DI {die Zieladresse hat sich um 1 Byte erhoeht}
@nowrap3g:
OUT DX,AX
{Wert nicht mehr pushen!}
DEC BP {BP:=16-rightcut}
@lastplane7:
{BP direkt verwenden}
SHR BP,1
SHR BP,1 {BX:=Bytes_je_Plane3_Zeile}
MOV AX,LINESIZE {Korrekturfaktor fuer Zeilenzieladressen}
SUB AX,BP { := LINESIZE-Bytes_je_Plane3_Zeile}
MOV DX,4 {Korrekturfaktor fuer Tilezeilenquelladressen}
SUB DX,BP { := 4-Bytes_je_Plane3_Zeile}
MOV BX,CX {BX:=Zeilenzaehler}
@eineZeile4g:
MOV CX,BP {CX:=Bytes_je_Plane0_Zeile}
REP MOVSB {eine Zeile uebertragen}
ADD DI,AX {DI auf naechste Zielzeile setzen}
ADD SI,DX {SI auf naechste Tilequellzeile setzen}
DEC BX
JNZ @eineZeile4g
{--- falls Abkuerzung genommen wurde, muss WriteMode 0 wieder}
{ gesetzt werden; hier der Einfachheit immer neugesetzt }
MOV AX,4005h
MOV DX,3CEh
OUT DX,AX
{---}
POP BP
MOV AX,SEG @Data {DS wiederherstellen}
MOV DS,AX
{Nun noch die Kacheln links und/oder rechts entlang des Bildschirmrandes:}
@do_raender:
CMP leftcut,0 {oder rightcut, denn da 320 durch 16 teilbar ist,}
JE @ende {ist leftcut=0 <=> rightcut=0 }
MOV AX,offsetXTiles
DEC AX
MOV xt,AX
MOV AX,ypix
MOV y,AX
INC tiles
MOV AX,offsetYTiles
MOV yt,AX
@repeat5:
MOV CL,1
OR AX,AX
JS @go10
CMP AX,YTiles
JAE @go10
DEC CL
@go10:
MOV offscreenFlag,CL
XOR SI,SI
DEC CL
JZ @go11
MOV AX,xt
OR AX,AX
JS @go11
CMP AX,XTiles
JAE @go11
MOV SI,RandIndex
@go11:
{PROCEDURE DrawLeftTile(y,leftcut:INTEGER; index:WORD);}
{ in: (0,y) = linke obere Ecke der zu zeichnenden Tile}
{ leftcut = Anzahl der links abzuschneidenden Tile-Spalten}
{ SI = index = Tilenummer}
{out: Tile wurde auf aktueller Seite PAGEADR gezeichnet}
{rem: Tile wurde links entsprechend geschnitten}
{ leftcut muss zwischen 0 und 15 liegen, 16 ist erlaubt (aber unsinnig)}
MOV AH,[OFFSET BackTile +SI] {AH:=BackTile[index]}
XOR AL,AL
SHR AH,1 {AX:=Kachel*64 =Kachel SHL 6 =(Kachel SHL 8) SHR 2}
RCR AL,1 {deshalb: AH:=Kachel und anschliessend AX um 2 Bit}
SHR AH,1 {rechtsschieben!}
RCR AL,1
MOV SI,AX {Das ist zugleich die (Offset-)Quelladresse in Page 3}
MOV DI,y {erste Zieladresse ist DI:=y*LINESIZE+(0 div 4)=y*LINESIZE}
SHL DI,1
MOV DI,CS:[OFFSET GADR + DI]
MOV AX,leftcut
MOV BX,AX {Kopie von leftcut in BX aufbewahren}
SHR AX,1
SHR AX,1
ADD SI,AX {SI um cutoff in Bytes weitersetzen=leftcut div 4}
{Jetzt wird keine Variable aus dem Stack mehr gebraucht: BP kann}
{verwendet werden!}
PUSH BP {wird beim Verlassen der Prozedur gebraucht!}
MOV BP,16+3
SUB BP,BX {BP:=16+3-leftcut, denn die Zahl der Bytes je Zeile fuer}
{Plane i berechnet sich zu (16+3-i-leftcut) SHR 2 }
MOV ES,PAGEADR {(Segment-)Zieladresse ist aktive Grafikseite}
MOV DS,SCROLLADR {(Segment-)Quelladresse ist die Seite SCROLLPAGE}
MOV DX,3CEh
AND BL,3
MOV AH,BL {AH:=leftcut mod 4}
MOV AL,4
JNE @mode0h {nur falls leftcut mod 4=0 ist koennen wir WriteMode1 verwenden}
{--- "Abkuerzung" ueber WriteMode 1 moeglich!: DX=3CEh, BP=16+3-leftcut}
{ Dass BP "3 zu gross ist" stoert nicht, da dieser Code nur ausge- }
{ fuehrt wird, wenn leftcut mod 4=0, also 16+3-leftcut=...11b, die }
{ "11b" werden also beim Rechtsschieben eh abgeschnitten!}
MOV AX,4105h
OUT DX,AX {WriteMode 1 waehlen}
MOV DX,3C4h
MOV AX,0F02h {alle 4 Planes gleichzeitig bearbeiten}
OUT DX,AX
JMP @lastPlane8 {abkuerzen}
{---}
@mode0h:
OUT DX,AX {aktuelle Leseplane waehlen}
PUSH AX {und fuer spaeter merken}
MOV DX,3C4h
MOV AX,0102h {Schreibplane 0 waehlen}
OUT DX,AX
MOV BX,BP
SHR BX,1
SHR BX,1 {BX:=Bytes_je_Plane0_Zeile = (16+3-leftcut) DIV 4}
MOV AX,LINESIZE {Korrekturfaktor fuer Zeilenzieladressen}
SUB AX,BX { := LINESIZE-Bytes_je_Plane0_Zeile}
MOV DX,4 {Korrekturfaktor fuer Tilezeilenquelladressen}
SUB DX,BX { := 4-Bytes_je_Plane0_Zeile}
MOV CX,BX {CX:=Bytes_je_Plane0_Zeile}
REP MOVSB {1.Zeile}
ADD DI,AX {DI auf naechste Zielzeile setzen}
ADD SI,DX {SI auf naechste Tilequellzeile setzen}
MOV CX,BX
REP MOVSB {2.Zeile}
ADD DI,AX
ADD SI,DX
MOV CX,BX
REP MOVSB {3.Zeile}
ADD DI,AX
ADD SI,DX
MOV CX,BX
REP MOVSB {4.Zeile}
ADD DI,AX
ADD SI,DX
MOV CX,BX
REP MOVSB {5.Zeile}
ADD DI,AX
ADD SI,DX
MOV CX,BX
REP MOVSB {6.Zeile}
ADD DI,AX
ADD SI,DX
MOV CX,BX
REP MOVSB {7.Zeile}
ADD DI,AX
ADD SI,DX
MOV CX,BX
REP MOVSB {8.Zeile}
ADD DI,AX
ADD SI,DX
MOV CX,BX
REP MOVSB {9.Zeile}
ADD DI,AX
ADD SI,DX
MOV CX,BX
REP MOVSB {10.Zeile}
ADD DI,AX
ADD SI,DX
MOV CX,BX
REP MOVSB {11.Zeile}
ADD DI,AX
ADD SI,DX
MOV CX,BX
REP MOVSB {12.Zeile}
ADD DI,AX
ADD SI,DX
MOV CX,BX
REP MOVSB {13.Zeile}
ADD DI,AX
ADD SI,DX
MOV CX,BX
REP MOVSB {14.Zeile}
ADD DI,AX
ADD SI,DX
MOV CX,BX
REP MOVSB {15.Zeile}
ADD DI,AX
ADD SI,DX
MOV CX,BX
REP MOVSB {16.Zeile}
ADD DI,AX
ADD SI,DX
MOV DX,3C4h
MOV AX,0202h {Schreibplane 1 waehlen}
OUT DX,AX
MOV DX,3CEh {naechste Leseplane:}
POP AX
INC AH
AND AH,3 {um 1 erhoehen MOD 4}
JNE @nowrap1h
INC SI {nach Plane 3 kommt wieder Plane 0, aber}
{die Quelladresse hat sich um 1 Byte erhoeht}
@nowrap1h:
OUT DX,AX
PUSH AX
SUB DI,16*LINESIZE {DI und SI wieder zuruecksetzen}
SUB SI,16*4
DEC BP {BP:=16+2-leftcut}
MOV BX,BP
SHR BX,1
SHR BX,1 {BX:=Bytes_je_Plane1_Zeile}
MOV AX,LINESIZE {Korrekturfaktor fuer Zeilenzieladressen}
SUB AX,BX { := LINESIZE-Bytes_je_Plane1_Zeile}
MOV DX,4 {Korrekturfaktor fuer Tilezeilenquelladressen}
SUB DX,BX { := 4-Bytes_je_Plane1_Zeile}
MOV CX,BX {CX:=Bytes_je_Plane1_Zeile}
REP MOVSB {1.Zeile}
ADD DI,AX {DI auf naechste Zielzeile setzen}
ADD SI,DX {SI auf naechste Tilequellzeile setzen}
MOV CX,BX
REP MOVSB {2.Zeile}
ADD DI,AX
ADD SI,DX
MOV CX,BX
REP MOVSB {3.Zeile}
ADD DI,AX
ADD SI,DX
MOV CX,BX
REP MOVSB {4.Zeile}
ADD DI,AX
ADD SI,DX
MOV CX,BX
REP MOVSB {5.Zeile}
ADD DI,AX
ADD SI,DX
MOV CX,BX
REP MOVSB {6.Zeile}
ADD DI,AX
ADD SI,DX
MOV CX,BX
REP MOVSB {7.Zeile}
ADD DI,AX
ADD SI,DX
MOV CX,BX
REP MOVSB {8.Zeile}
ADD DI,AX
ADD SI,DX
MOV CX,BX
REP MOVSB {9.Zeile}
ADD DI,AX
ADD SI,DX
MOV CX,BX
REP MOVSB {10.Zeile}
ADD DI,AX
ADD SI,DX
MOV CX,BX
REP MOVSB {11.Zeile}
ADD DI,AX
ADD SI,DX
MOV CX,BX
REP MOVSB {12.Zeile}
ADD DI,AX
ADD SI,DX
MOV CX,BX
REP MOVSB {13.Zeile}
ADD DI,AX
ADD SI,DX
MOV CX,BX
REP MOVSB {14.Zeile}
ADD DI,AX
ADD SI,DX
MOV CX,BX
REP MOVSB {15.Zeile}
ADD DI,AX
ADD SI,DX
MOV CX,BX
REP MOVSB {16.Zeile}
ADD DI,AX
ADD SI,DX
MOV DX,3C4h
MOV AX,0402h {Schreibplane 2 waehlen}
OUT DX,AX
MOV DX,3CEh {naechste Leseplane:}
POP AX
INC AH
AND AH,3 {um 1 erhoehen MOD 4}
JNE @nowrap2h
INC SI {nach Plane 3 kommt wieder Plane 0, aber}
{die Quelladresse hat sich um 1 Byte erhoeht}
@nowrap2h:
OUT DX,AX
PUSH AX
SUB DI,16*LINESIZE {DI und SI wieder zuruecksetzen}
SUB SI,16*4
DEC BP {BP:=16+1-leftcut}
MOV BX,BP
SHR BX,1
SHR BX,1 {BX:=Bytes_je_Plane2_Zeile}
MOV AX,LINESIZE {Korrekturfaktor fuer Zeilenzieladressen}
SUB AX,BX { := LINESIZE-Bytes_je_Plane2_Zeile}
MOV DX,4 {Korrekturfaktor fuer Tilezeilenquelladressen}
SUB DX,BX { := 4-Bytes_je_Plane2_Zeile}
MOV CX,BX {CX:=Bytes_je_Plane2_Zeile}
REP MOVSB {1.Zeile}
ADD DI,AX {DI auf naechste Zielzeile setzen}
ADD SI,DX {SI auf naechste Tilequellzeile setzen}
MOV CX,BX
REP MOVSB {2.Zeile}
ADD DI,AX
ADD SI,DX
MOV CX,BX
REP MOVSB {3.Zeile}
ADD DI,AX
ADD SI,DX
MOV CX,BX
REP MOVSB {4.Zeile}
ADD DI,AX
ADD SI,DX
MOV CX,BX
REP MOVSB {5.Zeile}
ADD DI,AX
ADD SI,DX
MOV CX,BX
REP MOVSB {6.Zeile}
ADD DI,AX
ADD SI,DX
MOV CX,BX
REP MOVSB {7.Zeile}
ADD DI,AX
ADD SI,DX
MOV CX,BX
REP MOVSB {8.Zeile}
ADD DI,AX
ADD SI,DX
MOV CX,BX
REP MOVSB {9.Zeile}
ADD DI,AX
ADD SI,DX
MOV CX,BX
REP MOVSB {10.Zeile}
ADD DI,AX
ADD SI,DX
MOV CX,BX
REP MOVSB {11.Zeile}
ADD DI,AX
ADD SI,DX
MOV CX,BX
REP MOVSB {12.Zeile}
ADD DI,AX
ADD SI,DX
MOV CX,BX
REP MOVSB {13.Zeile}
ADD DI,AX
ADD SI,DX
MOV CX,BX
REP MOVSB {14.Zeile}
ADD DI,AX
ADD SI,DX
MOV CX,BX
REP MOVSB {15.Zeile}
ADD DI,AX
ADD SI,DX
MOV CX,BX
REP MOVSB {16.Zeile}
ADD DI,AX
ADD SI,DX
MOV DX,3C4h
MOV AX,0802h {Schreibplane 3 waehlen}
OUT DX,AX
MOV DX,3CEh {naechste Leseplane:}
POP AX
INC AH
AND AH,3 {um 1 erhoehen MOD 4}
JNE @nowrap3h
INC SI {nach Plane 3 kommt wieder Plane 0, aber}
{die Quelladresse hat sich um 1 Byte erhoeht}
@nowrap3h:
OUT DX,AX
{Wert nicht mehr pushen!}
SUB DI,16*LINESIZE {DI und SI wieder zuruecksetzen}
SUB SI,16*4
DEC BP {BP:=16-leftcut}
@lastplane8:
{BP direkt verwenden}
SHR BP,1
SHR BP,1 {BP:=Bytes_je_Plane3_Zeile}
MOV AX,LINESIZE {Korrekturfaktor fuer Zeilenzieladressen}
SUB AX,BP { := LINESIZE-Bytes_je_Plane3_Zeile}
MOV DX,4 {Korrekturfaktor fuer Tilezeilenquelladressen}
SUB DX,BP { := 4-Bytes_je_Plane3_Zeile}
MOV CX,BP {CX:=Bytes_je_Plane2_Zeile}
REP MOVSB {1.Zeile}
ADD DI,AX {DI auf naechste Zielzeile setzen}
ADD SI,DX {SI auf naechste Tilequellzeile setzen}
MOV CX,BP
REP MOVSB {2.Zeile}
ADD DI,AX
ADD SI,DX
MOV CX,BP
REP MOVSB {3.Zeile}
ADD DI,AX
ADD SI,DX
MOV CX,BP
REP MOVSB {4.Zeile}
ADD DI,AX
ADD SI,DX
MOV CX,BP
REP MOVSB {5.Zeile}
ADD DI,AX
ADD SI,DX
MOV CX,BP
REP MOVSB {6.Zeile}
ADD DI,AX
ADD SI,DX
MOV CX,BP
REP MOVSB {7.Zeile}
ADD DI,AX
ADD SI,DX
MOV CX,BP
REP MOVSB {8.Zeile}
ADD DI,AX
ADD SI,DX
MOV CX,BP
REP MOVSB {9.Zeile}
ADD DI,AX
ADD SI,DX
MOV CX,BP
REP MOVSB {10.Zeile}
ADD DI,AX
ADD SI,DX
MOV CX,BP
REP MOVSB {11.Zeile}
ADD DI,AX
ADD SI,DX
MOV CX,BP
REP MOVSB {12.Zeile}
ADD DI,AX
ADD SI,DX
MOV CX,BP
REP MOVSB {13.Zeile}
ADD DI,AX
ADD SI,DX
MOV CX,BP
REP MOVSB {14.Zeile}
ADD DI,AX
ADD SI,DX
MOV CX,BP
REP MOVSB {15.Zeile}
ADD DI,AX
ADD SI,DX
MOV CX,BP
REP MOVSB {16.Zeile}
{--- falls Abkuerzung genommen wurde, muss WriteMode 0 wieder}
{ gesetzt werden; hier der Einfachheit immer neugesetzt }
MOV AX,4005h
MOV DX,3CEh
OUT DX,AX
{---}
POP BP
MOV AX,SEG @Data {DS wiederherstellen}
MOV DS,AX
MOV CL,offscreenFlag
XOR SI,SI
DEC CL
JZ @go12
MOV AX,xt
MOV DX,tiles
ADD AX,DX
JS @go12
CMP AX,XTiles
JAE @go12
MOV SI,RandIndex
ADD SI,DX
@go12:
{PROCEDURE DrawRightTile(x,y,rightcut:INTEGER; index:WORD);}
{ in: (x,y) = linke obere Ecke der zu zeichnenden Tile}
{ rightcut = Anzahl der rechts abzuschneidenden Tile-Spalten}
{ SI = index = Tilenummer}
{out: Tile wurde auf aktueller Seite PAGEADR gezeichnet}
{rem: Tile wurde rechts entsprechend geschnitten}
{ rightcut muss zwischen 0 und 15 liegen, 16 ist erlaubt (aber unsinnig)!}
{ rightcut koennte auch berechnet werden (fuer x>xmax-16) gemaess}
{ rightcut := x+15-xmax }
MOV AH,[OFFSET BackTile +SI] {AH:=BackTile[index]}
XOR AL,AL
SHR AH,1 {AX:=Kachel*64 =Kachel SHL 6 =(Kachel SHL 8) SHR 2}
RCR AL,1 {deshalb: AH:=Kachel und anschliessend AX um 2 Bit}
SHR AH,1 {rechtsschieben!}
RCR AL,1
MOV SI,AX {Das ist zugleich die (Offset-)Quelladresse in Page 3}
MOV DI,y {die erste Zieladresse ist DI:=y*LINESIZE+(x div 4)}
SHL DI,1
MOV DI,CS:[OFFSET GADR + DI]
MOV AX,x
MOV BX,AX {Kopie von x nach BX}
SHR AX,1
SHR AX,1
ADD DI,AX
MOV CX,rightcut
{Jetzt wird keine Variable aus dem Stack mehr gebraucht: BP kann}
{verwendet werden!}
PUSH BP {wird beim Verlassen der Prozedur gebraucht!}
MOV BP,16+3
SUB BP,CX {BP:=16+3-rightcut, denn die Zahl der Bytes je Zeile fuer}
{Plane i berechnet sich zu (16+3-i-rightcut) SHR 2 }
MOV ES,PAGEADR {(Segment-)Zieladresse ist aktive Grafikseite}
MOV DS,SCROLLADR {(Segment-)Quelladresse ist die Seite SCROLLPAGE}
MOV DX,3C4h
MOV AL,2
AND BX,3
JNE @mode0i {nur falls x mod 4=0 _und_ rightcut mod 4=0 ist koennen}
AND CX,3 {wir WriteMode1 verwenden!}
JNE @mode0i
{--- "Abkuerzung" ueber WriteMode 1 moeglich!: DX=3C4h, BP=16+3-rightcut}
MOV AH,0Fh
OUT DX,AX {alle 4 Planes gleichzeitig bearbeiten}
MOV DX,3CEh
MOV AX,4105h {WriteMode 1 waehlen}
OUT DX,AX
SUB BP,3 {BP auf die richtige Groesse bringen}
JMP @lastPlane9 {abkuerzen}
{---}
@mode0i:
MOV AH,CS:[OFFSET CS_TranslateTab +BX]
OUT DX,AX {aktuelle Schreibplane waehlen}
PUSH AX {und fuer spaeter merken}
MOV DX,3CEh
MOV AX,0004h {Leseplane 0 waehlen}
OUT DX,AX
MOV BX,BP
SHR BX,1
SHR BX,1 {BX:=Bytes_je_Plane0_Zeile = (16+3-rightcut) DIV 4}
MOV AX,LINESIZE {Korrekturfaktor fuer Zeilenzieladressen}
SUB AX,BX { := LINESIZE-Bytes_je_Plane0_Zeile}
MOV DX,4 {Korrekturfaktor fuer Tilezeilenquelladressen}
SUB DX,BX { := 4-Bytes_je_Plane0_Zeile}
MOV CX,BX {CX:=Bytes_je_Plane0_Zeile}
REP MOVSB {1.Zeile}
ADD DI,AX {DI auf naechste Zielzeile setzen}
ADD SI,DX {SI auf naechste Tilequellzeile setzen}
MOV CX,BX
REP MOVSB {2.Zeile}
ADD DI,AX
ADD SI,DX
MOV CX,BX
REP MOVSB {3.Zeile}
ADD DI,AX
ADD SI,DX
MOV CX,BX
REP MOVSB {4.Zeile}
ADD DI,AX
ADD SI,DX
MOV CX,BX
REP MOVSB {5.Zeile}
ADD DI,AX
ADD SI,DX
MOV CX,BX
REP MOVSB {6.Zeile}
ADD DI,AX
ADD SI,DX
MOV CX,BX
REP MOVSB {7.Zeile}
ADD DI,AX
ADD SI,DX
MOV CX,BX
REP MOVSB {8.Zeile}
ADD DI,AX
ADD SI,DX
MOV CX,BX
REP MOVSB {9.Zeile}
ADD DI,AX
ADD SI,DX
MOV CX,BX
REP MOVSB {10.Zeile}
ADD DI,AX
ADD SI,DX
MOV CX,BX
REP MOVSB {11.Zeile}
ADD DI,AX
ADD SI,DX
MOV CX,BX
REP MOVSB {12.Zeile}
ADD DI,AX
ADD SI,DX
MOV CX,BX
REP MOVSB {13.Zeile}
ADD DI,AX
ADD SI,DX
MOV CX,BX
REP MOVSB {14.Zeile}
ADD DI,AX
ADD SI,DX
MOV CX,BX
REP MOVSB {15.Zeile}
ADD DI,AX
ADD SI,DX
MOV CX,BX
REP MOVSB {16.Zeile}
ADD DI,AX
ADD SI,DX
MOV DX,3CEh
MOV AX,0104h {Leseplane 1 waehlen}
OUT DX,AX
MOV DX,3C4h {naechste Schreibplane:}
POP AX
SHL AH,1
CMP AH,16
JNE @nowrap1i
MOV AH,1 {nach Plane 3 kommt wieder Plane 0, aber}
INC DI {die Zieladresse hat sich um 1 Byte erhoeht}
@nowrap1i:
OUT DX,AX
PUSH AX
SUB DI,16*LINESIZE {DI und SI wieder zuruecksetzen}
SUB SI,16*4
DEC BP {BP:=16+2-rightcut}
MOV BX,BP
SHR BX,1
SHR BX,1 {BX:=Bytes_je_Plane1_Zeile}
MOV AX,LINESIZE {Korrekturfaktor fuer Zeilenzieladressen}
SUB AX,BX { := LINESIZE-Bytes_je_Plane1_Zeile}
MOV DX,4 {Korrekturfaktor fuer Tilezeilenquelladressen}
SUB DX,BX { := 4-Bytes_je_Plane1_Zeile}
MOV CX,BX {CX:=Bytes_je_Plane1_Zeile}
REP MOVSB {1.Zeile}
ADD DI,AX {DI auf naechste Zielzeile setzen}
ADD SI,DX {SI auf naechste Tilequellzeile setzen}
MOV CX,BX
REP MOVSB {2.Zeile}
ADD DI,AX
ADD SI,DX
MOV CX,BX
REP MOVSB {3.Zeile}
ADD DI,AX
ADD SI,DX
MOV CX,BX
REP MOVSB {4.Zeile}
ADD DI,AX
ADD SI,DX
MOV CX,BX
REP MOVSB {5.Zeile}
ADD DI,AX
ADD SI,DX
MOV CX,BX
REP MOVSB {6.Zeile}
ADD DI,AX
ADD SI,DX
MOV CX,BX
REP MOVSB {7.Zeile}
ADD DI,AX
ADD SI,DX
MOV CX,BX
REP MOVSB {8.Zeile}
ADD DI,AX
ADD SI,DX
MOV CX,BX
REP MOVSB {9.Zeile}
ADD DI,AX
ADD SI,DX
MOV CX,BX
REP MOVSB {10.Zeile}
ADD DI,AX
ADD SI,DX
MOV CX,BX
REP MOVSB {11.Zeile}
ADD DI,AX
ADD SI,DX
MOV CX,BX
REP MOVSB {12.Zeile}
ADD DI,AX
ADD SI,DX
MOV CX,BX
REP MOVSB {13.Zeile}
ADD DI,AX
ADD SI,DX
MOV CX,BX
REP MOVSB {14.Zeile}
ADD DI,AX
ADD SI,DX
MOV CX,BX
REP MOVSB {15.Zeile}
ADD DI,AX
ADD SI,DX
MOV CX,BX
REP MOVSB {16.Zeile}
ADD DI,AX
ADD SI,DX
MOV DX,3CEh
MOV AX,0204h {Leseplane 2 waehlen}
OUT DX,AX
MOV DX,3C4h {naechste Schreibplane:}
POP AX
SHL AH,1
CMP AH,16
JNE @nowrap2i
MOV AH,1 {nach Plane 3 kommt wieder Plane 0, aber}
INC DI {die Zieladresse hat sich um 1 Byte erhoeht}
@nowrap2i:
OUT DX,AX
PUSH AX
SUB DI,16*LINESIZE {DI und SI wieder zuruecksetzen}
SUB SI,16*4
DEC BP {BP:=16+1-rightcut}
MOV BX,BP
SHR BX,1
SHR BX,1 {BX:=Bytes_je_Plane2_Zeile}
MOV AX,LINESIZE {Korrekturfaktor fuer Zeilenzieladressen}
SUB AX,BX { := LINESIZE-Bytes_je_Plane2_Zeile}
MOV DX,4 {Korrekturfaktor fuer Tilezeilenquelladressen}
SUB DX,BX { := 4-Bytes_je_Plane2_Zeile}
MOV CX,BX {CX:=Bytes_je_Plane2_Zeile}
REP MOVSB {1.Zeile}
ADD DI,AX {DI auf naechste Zielzeile setzen}
ADD SI,DX {SI auf naechste Tilequellzeile setzen}
MOV CX,BX
REP MOVSB {2.Zeile}
ADD DI,AX
ADD SI,DX
MOV CX,BX
REP MOVSB {3.Zeile}
ADD DI,AX
ADD SI,DX
MOV CX,BX
REP MOVSB {4.Zeile}
ADD DI,AX
ADD SI,DX
MOV CX,BX
REP MOVSB {5.Zeile}
ADD DI,AX
ADD SI,DX
MOV CX,BX
REP MOVSB {6.Zeile}
ADD DI,AX
ADD SI,DX
MOV CX,BX
REP MOVSB {7.Zeile}
ADD DI,AX
ADD SI,DX
MOV CX,BX
REP MOVSB {8.Zeile}
ADD DI,AX
ADD SI,DX
MOV CX,BX
REP MOVSB {9.Zeile}
ADD DI,AX
ADD SI,DX
MOV CX,BX
REP MOVSB {10.Zeile}
ADD DI,AX
ADD SI,DX
MOV CX,BX
REP MOVSB {11.Zeile}
ADD DI,AX
ADD SI,DX
MOV CX,BX
REP MOVSB {12.Zeile}
ADD DI,AX
ADD SI,DX
MOV CX,BX
REP MOVSB {13.Zeile}
ADD DI,AX
ADD SI,DX
MOV CX,BX
REP MOVSB {14.Zeile}
ADD DI,AX
ADD SI,DX
MOV CX,BX
REP MOVSB {15.Zeile}
ADD DI,AX
ADD SI,DX
MOV CX,BX
REP MOVSB {16.Zeile}
ADD DI,AX
ADD SI,DX
MOV DX,3CEh
MOV AX,0304h {Leseplane 3 waehlen}
OUT DX,AX
MOV DX,3C4h {naechste Schreibplane:}
POP AX
SHL AH,1
CMP AH,16
JNE @nowrap3i
MOV AH,1 {nach Plane 3 kommt wieder Plane 0, aber}
INC DI {die Zieladresse hat sich um 1 Byte erhoeht}
@nowrap3i:
OUT DX,AX
{Wert nicht mehr pushen!}
SUB DI,16*LINESIZE {DI und SI wieder zuruecksetzen}
SUB SI,16*4
DEC BP {BP:=16-rightcut}
@lastplane9:
{BP direkt verwenden}
SHR BP,1
SHR BP,1 {BP:=Bytes_je_Plane3_Zeile}
MOV AX,LINESIZE {Korrekturfaktor fuer Zeilenzieladressen}
SUB AX,BP { := LINESIZE-Bytes_je_Plane3_Zeile}
MOV DX,4 {Korrekturfaktor fuer Tilezeilenquelladressen}
SUB DX,BP { := 4-Bytes_je_Plane3_Zeile}
MOV CX,BP {CX:=Bytes_je_Plane2_Zeile}
REP MOVSB {1.Zeile}
ADD DI,AX {DI auf naechste Zielzeile setzen}
ADD SI,DX {SI auf naechste Tilequellzeile setzen}
MOV CX,BP
REP MOVSB {2.Zeile}
ADD DI,AX
ADD SI,DX
MOV CX,BP
REP MOVSB {3.Zeile}
ADD DI,AX
ADD SI,DX
MOV CX,BP
REP MOVSB {4.Zeile}
ADD DI,AX
ADD SI,DX
MOV CX,BP
REP MOVSB {5.Zeile}
ADD DI,AX
ADD SI,DX
MOV CX,BP
REP MOVSB {6.Zeile}
ADD DI,AX
ADD SI,DX
MOV CX,BP
REP MOVSB {7.Zeile}
ADD DI,AX
ADD SI,DX
MOV CX,BP
REP MOVSB {8.Zeile}
ADD DI,AX
ADD SI,DX
MOV CX,BP
REP MOVSB {9.Zeile}
ADD DI,AX
ADD SI,DX
MOV CX,BP
REP MOVSB {10.Zeile}
ADD DI,AX
ADD SI,DX
MOV CX,BP
REP MOVSB {11.Zeile}
ADD DI,AX
ADD SI,DX
MOV CX,BP
REP MOVSB {12.Zeile}
ADD DI,AX
ADD SI,DX
MOV CX,BP
REP MOVSB {13.Zeile}
ADD DI,AX
ADD SI,DX
MOV CX,BP
REP MOVSB {14.Zeile}
ADD DI,AX
ADD SI,DX
MOV CX,BP
REP MOVSB {15.Zeile}
ADD DI,AX
ADD SI,DX
MOV CX,BP
REP MOVSB {16.Zeile}
{--- falls Abkuerzung genommen wurde, muss WriteMode 0 wieder}
{ gesetzt werden; hier der Einfachheit immer neugesetzt }
MOV AX,4005h
MOV DX,3CEh
OUT DX,AX
{---}
POP BP
MOV AX,SEG @Data {DS wiederherstellen}
MOV DS,AX
MOV AX,RandIndex
ADD AX,XTiles
MOV RandIndex,AX
MOV AX,yt
INC AX
MOV yt,AX
MOV DX,y
ADD DX,16
MOV y,DX
CMP DX,YMAX+1-16
JBE @repeat5
@ende:
{------- ab hier: Sprites auf aktuelle Grafikseite bringen}
@Sprites_zeichnen:
MOV SI,NMAX*2
PUSH BP {BP nachher wieder poppen!}
@zeichne:
{DS = normales Datensegment, ES = Grafikseitensegment, }
{SI = Spritepositionsnummer*2 }
@SZeich:
MOV BX,[SI + OFFSET SpriteN] {BX=SpriteN[?]=Spriteladenummer}
SHL BX,1 {BX = Spriteladenummer*2}
{Jetzt: "SpriteN[?]:=SpriteN[NextSprite[?]]" berechnen:}
MOV BX,[BX + OFFSET NextSprite] {AX=NextSprite[SpriteN[?]]}
MOV [SI + OFFSET SpriteN],BX {als neue SpriteN[?] uebernehmen}
SHL BX,1
JNZ @aktiv
JMP @noSprite
@aktiv:
PUSH SI {Spritepositionsnummer*2 retten}
MOV DX,[SI + OFFSET SpriteX] {if SpriteX>xmax then skip_sprite }
SUB DX,StartVirtualX {virtuelle -> absolute Koordinaten}
CMP DX,XMAX
JLE @L0
@ToSprite_fertig: {Sprungleiste zu @Sprite_fertig}
JMP @Sprite_fertig
@L0:
MOV CS:WORD PTR @akt_SpriteX+1,DX
MOV DI,[SI + OFFSET SpriteY] {DI = SpriteY_virtuell}
SUB DI,StartVirtualY {DI = SpriteY (absolut!)}
MOV DS,[BX + OFFSET SPRITEAD] {!!!DS = ^Spritedaten !!!}
MOV AX,[Breite] {AX = Breite in 4er-Gruppen}
MOV CS:WORD PTR @max_Breite+1,AX
MOV SI,AX {SI = dto.}
SHL AX,1
SHL AX,1 {AX = max_Breite_in_Punkten}
ADD AX,DX {AX = max_Breite_in_Punkten+SpriteX}
JS @ToSprite_fertig
MOV BX,DI {if SpriteY>=0 then starty:=+SpriteY}
NEG DI { else starty:=-SpriteY}
MOV BP,DI
JG @Top_cut
XOR DI,DI
@Top_cut: {DI = starty, BP = -SpriteY}
MOV AX,[Hoehe] {AX = Hoehe (in Zeilen) }
CMP DI,AX {if starty>=Hoehe then skip_sprite}
JGE @ToSprite_fertig
ADD BP,YMAX {BP = -SpriteY+ymax}
JL @ToSprite_fertig {(etwas frei:) }
CMP AX,BP {if Hoehe+SpriteY>ymax }
JG @To_then { then [ endy:=199-SpriteY }
DEC AX { if endy<0 then skip_sprite ] }
MOV BP,AX { else endy:=Hoehe-1 }
{BP = endy, SI=[@max_Breite+1] = max_Breite_in_4er_Gruppen, }
{DI = starty, BX = SpriteY, DX=[@akt_SpriteX+1] = SpriteX, }
{DS = ^Spritedaten, ES = ^Grafikseite}
@To_then:
MOV AX,BP
SUB BP,DI
SHL BP,1
MOV [End_min_Start],BP {= (endy-starty)*2 =Yaktuell*2}
ADD BX,AX
SHL BX,1
MOV BX,CS:[OFFSET gadr + BX] {BX=zeilenadr:=(endy+SpriteY)*LINESIZE}
MOV [zeilenadr],BX {auch nach [zeilenadr] }
MOV BP,DX
MUL SI {AX = endy*max_breite_in_4er =yoffset}
MOV [yoffset_],AX {auch nach [yoffset_]}
SHL DI,1 {DI = starty*2}
MOV CS:WORD PTR @Starty_2+1,DI {auch nach [@Starty_2+1] }
{kleiner Einschub: anhand des Modusbytes des Sprites entscheiden, ob}
{eine andere Routine zur Darstellung des Sprites als die gerade ak- }
{tive benoetigt wird und wenn ja, diese in Position bringen! }
{Verwendete Register: AX und SI }
MOV AL,[Modus] {Modusbyte des Sprites holen}
XOR AH,AH
SHL AX,1
MOV SI,AX
MOV SI,CS:[OFFSET Adressen +SI] {Pointer auf zugehoerige Routine holen}
MOV AX,CS:[SI]
CMP AX,CS:[WORD PTR @Patch1] {ist diese Routine bereits aktiv?}
JE @no_newcode {ja, nix zu tun}
PUSH DS {nein, kopiere die Routine an die}
PUSH CS {entsprechenden Stellen}
POP DS
MOV [WORD PTR @Patch1],AX
MOV [WORD PTR @Patch2],AX
MOV [WORD PTR @Patch3],AX
MOV [WORD PTR @Patch4],AX
INC SI
INC SI
LODSW
MOV [WORD PTR @Patch1+2],AX
MOV [WORD PTR @Patch2+2],AX
MOV [WORD PTR @Patch3+2],AX
MOV [WORD PTR @Patch4+2],AX
LODSW
MOV [WORD PTR @Patch1+4],AX
MOV [WORD PTR @Patch2+4],AX
MOV [WORD PTR @Patch3+4],AX
MOV [WORD PTR @Patch4+4],AX
LODSW
MOV [WORD PTR @Patch1+6],AX
MOV [WORD PTR @Patch2+6],AX
MOV [WORD PTR @Patch3+6],AX
MOV [WORD PTR @Patch4+6],AX
LODSW
MOV [WORD PTR @Patch1+8],AX
MOV [WORD PTR @Patch2+8],AX
MOV [WORD PTR @Patch3+8],AX
MOV [WORD PTR @Patch4+8],AX
LODSW
MOV [WORD PTR @Patch1+10],AX
MOV [WORD PTR @Patch2+10],AX
MOV [WORD PTR @Patch3+10],AX
MOV [WORD PTR @Patch4+10],AX
LODSW
MOV [WORD PTR @Patch1+12],AX
MOV [WORD PTR @Patch2+12],AX
MOV [WORD PTR @Patch3+12],AX
MOV [WORD PTR @Patch4+12],AX
LODSW
MOV [WORD PTR @Patch1+14],AX
MOV [WORD PTR @Patch2+14],AX
MOV [WORD PTR @Patch3+14],AX
MOV [WORD PTR @Patch4+14],AX
POP DS {DS wiederherstellen}
@no_newcode:
{(AX=)[yoffset_] = yoffset }
{ BX=[zeilenadr] = (endy+SpriteY)*LINESIZE}
{ DI=[@Starty_2+1] = starty*2}
{(SI=[@max_Breite+1] = max_Breite_in_4er_) }
{ BP = SpriteX}
{ DS = ^Spritedaten}
{ ES = ^Grafikseite}
{ [end_min_start] = (endy-starty)*2 =Yaktuell*2}
{ [@max_Breite+1] = max_Breite_in_4er_Gruppen }
{ [@akt_SpriteX+1] = SpriteX}
@eine_Zeile:
MOV SI,[end_min_start] {SI = Yaktuell*2 }
ADD SI,DI {startx:=sprite[WORD PTR sprite[L]+ }
MOV DI,SI { (Yaktuell+starty)*2] }
ADD SI,[Left]
MOV SI,[SI] {SI = startx, DI = (Yaktuell+starty)*2}
MOV AX,BP
ADD AX,SI {AX=bildschirmstartx:=SpriteX+startx }
CMP AX,XMAX {if bildschirmstartx>xmax then skip_zeile}
JG @ToZeile_fertig
MOV CX,SI {CX=startx}
OR AX,AX {licutoff_in_Punkten:=startx}
JGE @L1 {if bildschirmstartx<0 then }
SUB SI,AX { [dec(startx,bildschirmstartx) }
XOR AX,AX { bildschirmstartx:=0 }
MOV CX,BP { licutoff_in_Punkten:=-SpriteX] }
NEG CX
@L1: {CX=[licutoff_]= licutoff_in_Punkten, }
MOV [licutoff_],CX {SI = startx, AX = bildschirmstartx }
ADD DI,[Right]
MOV DI,[DI] {DI=endx:=sprite[WORD PTR sprite[R]+ }
{ (Yaktuell+starty)*2] }
MOV DX,BP
NEG DX {DX = -SpriteX }
MOV BP,DI
SUB BP,SI {BP = endx-startx }
SUB DX,DI
ADD DX,XMAX {DX=ueberhang:=xmax-(SpriteX+endx) }
JNS @kein_Ueberhang_rechts
ADD BP,DX
@kein_Ueberhang_rechts: {BP = sichtbare Breite dieser Zeile -1}
OR BP,BP
JNS @L6
@ToZeile_fertig:
JMP @Zeile_fertig {if Breite<=0 then skip_zeile }
@L6:
ADD BP,4
{ AX = bildschirmstartx}
{ BX=[zeilenadr] = (endy+SpriteY)*LINESIZE }
{ CX=[licutoff_] = licutoff_in_Punkten}
{(DX = (negativer) ueberhang (falls Wert<0) ) }
{(SI = startx) }
{(DI = endx) }
{ BP = Breite fuer diese Zeile in Punkten +3 }
{ DS = ^Spritedaten}
{ ES = ^Grafikseite}
{ [@max_Breite+1] = max_Breite_in_4er_) }
{ [end_min_start] = (endy-starty)*2 =Yaktuell*2}
{ [@Starty_2+1] = starty*2}
{ [@max_Breite+1] = max_Breite_in_4er_Gruppen, }
{ [@akt_SpriteX+1] = SpriteX}
MOV [bildx],AX {bildschirmstartx retten}
MOV DX,CX {DX = licutoff_in_Punkten}
MOV CX,BP
SHR CX,1
SHR CX,1 {CX = Breite DIV 4}
JCXZ @Plane1
{SI=Quellzeiger:=sprite[WORD PTR (licutoff_in_Punkten+0 AND 3)*2 }
{ +(licutoff_in_Punkten+0) DIV 4 +yoffset }
MOV SI,DX
AND SI,3
SHL SI,1 {SI = ((licutoff_in_Punkten+0) AND 3)*2}
MOV SI,[SI]
MOV DI,DX
SHR DI,1
SHR DI,1
ADD SI,DI
ADD SI,[yoffset_] {SI = sprite[WORD PTR (licutoff_...)] }
{ +(licutoff_in_Punkten+i) DIV 4 }
{ +yoffset }
{DI=Zielzeiger:=(bildschirmstartx+0) DIV 4 +zeilenadr}
MOV DI,AX {DI = bildschirmstartx }
SHR DI,1
SHR DI,1
ADD DI,BX
MOV BL,AL
AND BX,3 {BX = (bildschirmstartx+i) AND 3 }
MOV AH,Translate[BX] {AH = 1,2,4,8 fuer BX=0,1,2,3 }
MOV AL,2
MOV DX,3C4h
OUT DX,AX {Plane auswaehlen}
XCHG BX,DI
{CX Bytes von DS:SI nach ES:BX uebertragen }
{Hierher kommt die Routine zur Datenuebertragung!}
@Patch1:
db 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
@Plane1:
MOV DX,[bildx]
INC DX {DX = bildschirmstartx+1}
MOV BX,DX
SHR BX,1
SHR BX,1 {BX=zielzeiger:=(bildschirmstartx+1) }
ADD BX,[zeilenadr] { DIV 4 +zeilenadr }
MOV CX,BP
DEC CX {CX = Breite dieser Zeile +3 -1 }
SHR CX,1
SHR CX,1 {CX = Bytes_zu_moven fuer i=1 }
JCXZ @Plane2
MOV DI,[licutoff_]
INC DI {DI = (licutoff_in_Punkten+1) }
MOV SI,DI
AND SI,3
SHL SI,1 {SI = ((licutoff_in_Punkten+1) AND 3)*2}
MOV SI,[SI] {SI = sprite[WORD PTR licutoff_...] }
SHR DI,1 { +(licutoff_in_Punkten+1) DIV 4 }
SHR DI,1 { +yoffset }
ADD SI,DI
ADD SI,[yoffset_] {SI = Quellzeiger, }
{DI = (licutoff_in_Punkten+1) DIV 4 }
MOV DI,DX {DI = bildschirmstartx+1}
AND DI,3 {DI = (bildschirmstartx+1) AND 3 }
MOV AH,Translate[DI] {Maske fuer Portzugriff laden}
MOV AL,2
MOV DX,3C4h {Plane anwaehlen}
OUT DX,AX
{Hierher kommt die Routine zur Datenuebertragung!}
@Patch2:
db 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
@Plane2:
MOV DX,[bildx]
ADD DX,2
MOV BX,DX
SHR BX,1
SHR BX,1
ADD BX,[zeilenadr]
MOV CX,BP
SUB CX,2
SHR CX,1
SHR CX,1
JCXZ @Plane3
MOV DI,[licutoff_]
ADD DI,2
MOV SI,DI
AND SI,3
SHL SI,1
MOV SI,[SI]
SHR DI,1
SHR DI,1
ADD SI,DI
ADD SI,[yoffset_]
MOV DI,DX {DI = bildschirmstartx+2}
AND DI,3 {DI = (bildschirmstartx+1) AND 3 }
MOV AH,Translate[DI]
MOV AL,2
MOV DX,3C4h
OUT DX,AX
{Hierher kommt die Routine zur Datenuebertragung!}
@Patch3:
db 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
@Plane3:
MOV DX,[bildx]
ADD DX,3
MOV BX,DX
SHR BX,1
SHR BX,1
ADD BX,[zeilenadr]
MOV CX,BP
SUB CX,3
SHR CX,1
SHR CX,1
JCXZ @Zeile_fertig
MOV DI,[licutoff_]
ADD DI,3
MOV SI,DI
AND SI,3
SHL SI,1
MOV SI,[SI]
SHR DI,1
SHR DI,1
ADD SI,DI
ADD SI,[yoffset_]
MOV DI,DX {DI = bildschirmstartx+3}
AND DI,3 {DI = (bildschirmstartx+1) AND 3 }
MOV AH,Translate[DI]
MOV AL,2
MOV DX,3C4h
OUT DX,AX
{Hierher kommt die Routine zur Datenuebertragung!}
@Patch4:
db 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
@Zeile_fertig:
MOV AX,[yoffset_]
@max_Breite:
SUB AX,1234
MOV [yoffset_],AX
MOV BX,[zeilenadr]
SUB BX,LINESIZE
MOV [zeilenadr],BX
SUB WORD PTR [end_min_start],2
JS @Sprite_fertig
@Starty_2:
MOV DI,1234
@akt_SpriteX:
MOV BP,1234
JMP @eine_Zeile
@Sprite_fertig:
POP SI
MOV AX,SEG @Data
MOV DS,AX
@noSprite:
DEC SI
DEC SI
JS @fertig
JMP @zeichne
@fertig:
POP BP
{Die Grafikseite ist nun fertiggestellt und muss noch angezeigt werden:}
cli
mov dx,3DAh
@WaitNotVSyncLoop:
in al,dx
and al,8
jnz @WaitNotVSyncLoop
@WaitVSyncLoop:
in al,dx
and al,8
jz @WaitVSyncLoop
MOV DX,$3D4 {CRT-Controller}
MOV AL,$0D {LB-Startadress-Register}
OUT DX,AL
INC DX
{ Realisiere "AX:=Offset_Adr[Page]": }
MOV SI,PAGE {Page-Wert *2 (da Worteintraege!)}
MOV BX,SI {Page-Wert in BX merken!}
SHL SI,1 {dazu Startadresse des Feldes addieren}
ADD SI,OFFSET Offset_Adr-StartIndex*2 {evtl. Verschiebung korrigieren}
LODSW {und Wert holen}
OUT DX,AL {LB der neuen Startadresse setzen}
DEC DX
MOV AL,$0C
OUT DX,AL
INC DX
MOV AL,AH {HB der neuen Startadresse setzen}
OUT DX,AL
STI
NEG BX {neuer PAGE-Wert := 1-alter PAGE-Wert, d.h.: }
ADD BX,1 {IF PAGE=0 THEN PAGE:=1 ELSE (PAGE=1) PAGE:=0 }
MOV PAGE,BX
SHL BX,1 {neuer PAGEADR-Wert := Segment_Adr[PAGE] }
ADD BX,OFFSET Segment_Adr-StartIndex*2
MOV AX,[BX]
MOV PAGEADR,AX
{Jetzt ueberpruefen, ob gesetzte Zyklus(mindest)zeit abgelaufen ist:}
@L10:
MOV AL,TimeFlag {Bit 7 = 0/1 fuer Zeit ist abgelaufen/laeuft noch }
AND AL,$80
JE @L10
{Zeitueberwachung fuer naechsten Zyklus starten:}
MOV AL,IsAT {ist das ein AT/386? ($0/$80=ja/nein)}
OR AL,AL {Zeitmechanismus geht nur auf AT/386 }
JNE @L11 {anderenfalls keine Zeitueberwachung!}
MOV TimeFlag,AL {AL=0 zugleich als Init-Wert benutzen}
MOV DX,WORD PTR CycleTime {Mindestzykluszeit (in Mikrosekunden)}
MOV CX,WORD PTR CycleTime+2 {eintragen: CX=HIGH-Word, DX=LOW-Word}
MOV BX,OFFSET TimeFlag {ES:BX=Zeiger auf TimeFlag, Bit 7=0/1}
MOV AX,DS {fuer: Zeit laeuft noch/ist um }
MOV ES,AX
MOV AX,8300h {Zeitueberwachung starten}
INT 15h
@L11:
END
END;
FUNCTION LoadSprite(name:String; number:WORD):WORD;
{ in: name = Name des zu ladenden Sprite-Files (Typ: "*.COD" / "*.LIB" )}
{ number = Nummer, die das erste Sprite aus diesem File bekommen soll }
{out: Anzahl der aus dem File gelesenen Sprites (0 = Fehler trat auf) }
{rem: Die Routine erkennt automatisch, ob es sich bei dem File um ein ein-}
{ zelnes Sprite oder eine ganze Spritebibliothek handelt und laedt }
{ alle Spritedaten auf den Heap, und zwar derart, dass die Adresse }
{ immer auf eine Segmentgrenze fällt. Diese Anfangsadressen werden }
{ dann in der Tabelle SPRITEAD[number] abgelegt; sind mehrere Sprites }
{ in der Datei so werden sie mit fortlaufender Nummer eingetragen, }
{ also number+i }
LABEL quit_loop;
VAR p1,p2:Pointer;
len:LONGINT;
f:File;
count,Kopf:WORD;
Header:SpriteHeader;
BEGIN
count:=0; {Zahl der bisher eingelesenen Sprites}
Kopf:=SizeOf(SpriteHeader);
assign(f,name);
{$I-} reset(f,1); {$I+}
if (ioresult<>0)
THEN BEGIN {Datei existiert nicht oder nicht unter diesem Pfad}
Error:=Err_FileIO;
loadSprite:=0; exit
END;
len:=filesize(f); {Dateilaenge ermitteln}
if (maxavail<len+16)
THEN BEGIN
Error:=Err_NotEnoughMemory;
goto quit_loop;
END;
if (number=0) or (number>LoadMAX)
THEN BEGIN
Error:=Err_InvalidSpritenumber;
goto quit_loop;
END;
WHILE NOT EOF(f) DO
BEGIN
{Zunaechst den Spriteheader einlesen: }
{$I-} {jetzt den Spriteheader vià BLOCKREAD auf den Heap laden}
blockread(f,Header,Kopf);
{$I+}
IF (ioresult<>0)
THEN BEGIN
Error:=Err_FileIO;
goto quit_loop;
END;
IF (Header.Kennung[1]<>'K') or (Header.Kennung[2]<>'R')
THEN BEGIN
Error:=Err_NoSprite;
goto quit_loop;
END;
IF (Header.SpriteLength>MaxAvail+15) {noch genug Platz da?}
THEN BEGIN
Error:=Err_NotEnoughMemory;
goto quit_loop;
END;
{Jetzt eigentliche Spritedaten einlesen: }
getmem(p1,Header.SpriteLength+15); {genug Platz reservieren}
IF (LONGINT(p1) mod 16)=0
THEN p2:=p1 {p2 auf Segmentgrenze bringen}
ELSE LONGINT(p2):=LONGINT(p1) + (16-LONGINT(p1) mod 16);
MOVE(Header,p2^,Kopf); {Spriteheader auf Heap bringen}
LONGINT(p1):=LONGINT(p2)+Kopf; {zeigt genau hinter den Header}
{$I-} {jetzt den "Rest" des Sprites laden}
blockread(f,p1^,Header.SpriteLength-Kopf);
{$I+}
IF (ioresult<>0)
THEN BEGIN
Error:=Err_FileIO;
goto quit_loop;
END;
{der Spritenummer zuordnen:}
spritead[number+count]:=(longint(p2) shr 16)
+(longint(p2) and 65535) shr 4;
INC(count);
END;
quit_loop: ;
close(f);
loadSprite:=count
END;
FUNCTION LoadTile(name:STRING; number:BYTE):WORD;
{ in: name = Name des zu ladenden Sprite-Files (Typ: "*.COD" / "*.LIB" )}
{ number = 0..255 = Tilenummer fuer das erste Sprite der Datei }
{out: Anzahl der aus dem File gelesenen Tiles (0 = Fehler trat auf) }
{rem: Die Routine erkennt automatisch, ob es sich bei dem File um ein ein-}
{ zelnes Sprite oder eine ganze Spritebibliothek handelt und laedt }
{ alle Sprites, zerlegt diese in Tiles und legt sie in der 4.Grafik- }
{ seite ab, beginnend mit der uebergebenen Nummer number }
{ Da eine Kachel 16x16 Punkte gross ist, muessen die Sprites ein Viel-}
{ faches von 16 Punkten in x- und y-Richtung sein }
{ Enthaelt die Datei mehrere Tiles, so werden sie zeilenweise geladen,}
{ jede Zeile dabei in der Reihenfolge von links nach rechts }
LABEL quit_loop;
TYPE split=RECORD loword,hiword:WORD END;
VAR p1:Pointer;
len,ad,p:LONGINT;
f:File;
count,Kopf,ZielOfs,step,yoffset:WORD;
pSeg,pOfs:ARRAY[0..3] OF WORD;
Breite_in_Tiles,Hoehe_in_Tiles,x,y,i,zeilen:BYTE;
Header:SpriteHeader;
BEGIN
count:=0; {Zahl der bisher eingelesenen Sprites}
Kopf:=SizeOf(SpriteHeader);
assign(f,name);
{$I-} reset(f,1); {$I+}
if (ioresult<>0)
THEN BEGIN {Datei existiert nicht oder nicht unter diesem Pfad}
Error:=Err_FileIO;
LoadTile:=0; exit
END;
len:=filesize(f); {Dateilaenge ermitteln}
if (maxavail<len+16)
THEN BEGIN
Error:=Err_NotEnoughMemory;
goto quit_loop;
END;
WHILE NOT EOF(f) DO
BEGIN
{Zunaechst den Spriteheader einlesen: }
{$I-} {jetzt den Spriteheader vià BLOCKREAD auf den Heap laden}
blockread(f,Header,Kopf);
{$I+}
IF (ioresult<>0)
THEN BEGIN
Error:=Err_FileIO;
goto quit_loop;
END;
IF (Header.Kennung[1]<>'K') or (Header.Kennung[2]<>'R')
THEN BEGIN
Error:=Err_NoTile; {oder Err_NoSprite!}
goto quit_loop
END;
IF (Header.Breite_in_4er_Gruppen MOD 4<>0) OR
(Header.Hoehe_in_Zeilen MOD 16<>0) {Groesse Vielfaches von 16?}
THEN BEGIN
Error:=Err_NoTile;
goto quit_loop
END
ELSE BEGIN {ja, Anzahl Tiles in diesem Spritefile ermitteln}
Breite_in_Tiles:=Header.Breite_in_4er_Gruppen SHR 2;
Hoehe_in_Tiles :=Header.Hoehe_in_Zeilen SHR 4;
step:=Breite_in_Tiles*4; {Schrittweite beim laden}
END;
IF (Header.SpriteLength>MaxAvail) {noch genug Platz da?}
THEN BEGIN
Error:=Err_NotEnoughMemory;
goto quit_loop;
END;
{Jetzt eigentliche Spritedaten einlesen: }
getmem(p1,Header.SpriteLength); {genug Platz reservieren}
{$I-} {jetzt den "Rest" des Sprites laden}
blockread(f,p1^,Header.SpriteLength-Kopf);
{$I+}
IF (ioresult<>0)
THEN BEGIN
Error:=Err_FileIO;
goto quit_loop;
END;
ad:=(LONGINT(split(p1).HiWord) SHL 4) + split(p1).LoWord - Kopf;
FOR i:=0 TO 3 DO
BEGIN
p:=ad+Header.Zeiger_auf_Plane[i]; pSeg[i]:=p SHR 4; pOfs[i]:=p AND $F;
END;
FOR y:=0 TO Pred(Hoehe_in_Tiles) DO
BEGIN
yoffset:=y*Breite_in_Tiles*16*(16 DIV 4);
FOR x:=0 TO Pred(Breite_in_Tiles) DO
BEGIN
IF count+number>255
THEN BEGIN
Error:=Err_InvalidTileNumber;
goto quit_loop
END;
ZielOfs:=(number+count) SHL 6;
FOR i:=0 TO 3 DO
BEGIN
PORTW[$3C4]:=(TranslateTab[i] SHL 8) + 2;
FOR zeilen:=0 TO 15 DO
BEGIN
move(mem[pSeg[i]:pOfs[i] + yoffset + zeilen*step + x*(16 DIV 4)],
mem[SCROLLADR:ZielOfs + zeilen*(16 DIV 4)],
16 DIV 4);
END;
END;
INC(count);
END;
END;
Dispose(p1);
END;
quit_loop: ;
close(f);
LoadTile:=count
END;
PROCEDURE SetBackgroundScrollRange(x1,y1,x2,y2:INTEGER);
{ in: (x1,y1) = linke obere Ecke des Bereiches (virtuelle Koord.)}
{ (x2,y2) = dto., rechte untere Ecke}
{out: (BackX1,BackY1), (BackX2,BackY2) = auf 16er Raster gerundete Koord. }
{ XTiles, YTiles = Breite und Hoehe des gewaehlten Bereiches in Kacheln}
{rem: Die li. obere Ecke wird nach links oben gezogen, die re. untere nach }
{ rechts unten!}
{ Die Anwendung dieser Routine ist natuerlich nur fuer den Hintergrund-}
{ modus SCROLLING sinnvoll}
BEGIN
BackX1:=x1 AND $FFF0; BackX2:=x2 OR $F;
BackY1:=y1 AND $FFF0; BackY2:=y2 OR $F;
xtiles:=succ(BackX2-BackX1) shr 4;
ytiles:=succ(BackY2-BackY1) shr 4;
IF (xtiles OR ytiles)<=0
THEN Error:=Err_InvalidCoordinates
ELSE IF xtiles*ytiles>MaxTiles
THEN Error:=Err_BackgroundToBig;
END;
PROCEDURE SetBackgroundMode(mode:BYTE);
{ in: mode = gewuenschter Hintergrundmodus STATIC oder SCROLLING}
{out: Backgroundmode = gesetzter Modus STATIC/SCROLLING}
BEGIN
IF (mode<>STATIC) AND (mode<>SCROLLING)
THEN Error:=Err_InvalidMode
ELSE Backgroundmode:=mode
END;
PROCEDURE PutTile(x,y:INTEGER; TileNr:BYTE);
{ in: x,y = virtuelle Koordinate, an die die Kachel plaziert werden soll}
{out: TileNr= Nummer der zu plazierenden Kachel}
{rem: Der Punkt (x,y) wird zunaechst auf 16er Raster gebracht! }
{ Die Anwendung dieser Routine ist nur fuer den Hintergrund-}
{ modus SCROLLING sinnvoll}
VAR index:WORD;
BEGIN
ASM
MOV AX,x {berechne relativen X-Abstand vom linken Rand des}
SUB AX,BackX1 {definierten Bereiches in "x" mittels der Formel:}
SAR AX,1 { x:=((x AND $FFF0)-BackX1) DIV 16 (nicht SHR 4)!}
SAR AX,1 {"AND $FFF0" kann dabei entfallen, da in BackX1 }
SAR AX,1 {die letzte Hex-Ziffer $0 ist! }
SAR AX,1
MOV x,AX
MOV AX,y {dto. fuer Abstand der y-Koordinate vom oberen }
SUB AX,BackY1 {Rand: y:=((y AND $FFF0)-BackY1) DIV 16 }
SAR AX,1
SAR AX,1
SAR AX,1
SAR AX,1
MOV y,AX
END;
IF (x<0) OR (x>=XTiles) OR (y<0) OR (y>=YTiles)
THEN Error:=Err_InvalidCoordinates
ELSE BEGIN {jede Kachelzeile ist XTiles breit, jede Kachel 16x16 Punkte}
index:=y*XTiles+x; {eigentlich: (x MOD XTiles)}
BackTile[index]:=TileNr;
END;
END;
PROCEDURE SetModeByte(Sp:WORD; M:BYTE);
{ in: Sp = SpriteLADEnummer, dessen Modusbyte veraendert werden soll}
{out: M = Methode, mit der Sp ab sofort dargestellt werden soll: }
{ Display_NORMAL, Display_FAST oder Display_SHADOW }
{rem: Existiert das Sprite noch nicht oder ist der Modus nicht er- }
{ laubt, so geschieht nicht! }
VAR ad:WORD;
BEGIN
ad:=SPRITEAD[Sp];
IF ad=0 THEN Error:=Err_InvalidSpriteNumber {Sprite muss schon geladen sein}
ELSE IF (M<>Display_NORMAL) AND (M<>Display_FAST) AND (M<>Display_SHADOW)
THEN Error:=Err_InvalidMode {nur diese 3 Modi sind zulaessig}
ELSE MEM[ad:Modus]:=M
END;
FUNCTION GetModeByte(Sp:WORD):BYTE;
{ in: Sp = Spritenummer, dessen Modusbyte abgefragt werden soll}
{out: Methode, die fuer Sp momentan gesetzt ist: Display_NORMAL, }
{ Display_FAST, Display_SHADOW bzw. Display_UNKNOWN, wenn das }
{ Sprite noch nicht geladen wurde!}
VAR ad:WORD;
BEGIN
ad:=SPRITEAD[Sp];
IF (ad=0)
THEN GetModeByte:=Display_UNKNOWN {Sprite noch nicht geladen}
ELSE GetModeByte:=MEM[SPRITEAD[Sp]:Modus]
END;
PROCEDURE FillPage(pa,color:Byte);
{ in: pa = die Seite, die gefuellt werden soll (0..3)}
{ color = Fuellfarbe fuer die Seite}
{out: Grafikseite "pa" wurde mit der Farbe "Color" gefuellt }
{rem: Sinnvoll sind nur die Seiten 0,1 und BACKGNDPAGE, jedoch }
{ ist SCROLLPAGE ebenfalls erlaubt}
BEGIN
IF (pa<>0) AND (pa<>1) AND (pa<>BACKGNDPAGE) AND (pa<>SCROLLPAGE)
THEN Error:=Err_InvalidPageNumber
ELSE BEGIN
portw[$3C4]:=$0F02; {im Map-Mask Register alle 4 Ebenen selektieren}
fillchar(MEM[Segment_Adr[pa]:0],PAGESIZE,Color)
END;
END;
PROCEDURE FillBackground(color:BYTE);
{ in: color = Fuellfarbe fuer die Hintergrundseite BACKGNDPAGE }
{out: Die Grafikseite BACKGNDPAGE wurde mit der Farbe "Color" gefuellt}
{rem: Die Routine ist nur fuer den Hintergrundmodus STATIC sinnvoll }
BEGIN
FillPage(BACKGNDPAGE,color)
END;
PROCEDURE GetBackgroundFromPage(pa:Byte);
{in : pa = 0 oder 1 }
{out: - }
{rem: Der Hintergrundspeicher BACKGNDPAGE wird mit dem Inhalt der an- }
{ gegebenen Grafikseite gefuellt.}
{ Die Routine ist nur fuer den Hintergrundmodus STATIC sinnvoll }
VAR p:POINTER;
BEGIN
IF (pa<>0) AND (pa<>1)
THEN Error:=Err_InvalidPageNumber
ELSE BEGIN
portw[$3c4]:=$0f02; {im MapMask Register alle 4 Ebenen selektieren}
port[$3ce]:=5; {Schreibmodus 1 }
port[$3cf]:=port[$3cf] OR 1; {oder direkt :=$41}
p:=Ptr(Segment_Adr[pa],$0000);
MOVE(p^,MEM[BACKGNDADR:0],PAGESIZE);
portw[$3cf]:=port[$3cf] and $FC; {Schreibmodus 0 (oder direkt:=$40)}
END;
END;
PROCEDURE WritePage(name:STRING; pa:BYTE);
{ in: name = Filename fuer das abzuspeichernde Bild}
{ pa = abzuspeichernde Seite (0..2) }
{ PAGESIZE = Groesse einer Bitplane }
{ PICHeader= einzutragende Kennung fuer Bilderdateien}
{out: - }
{rem: Die Grafik auf Seite "pa" wurde in der Datei "name" als Bitmap abgelegt}
{ Diese Datei ist 4*PAGESIZE+3 = 64003 Bytes gross: 320x200 Punkte zu je }
{ 1 Byte plus Length(PICHeader) als Kennung}
VAR f:FILE;
i,oldMode:BYTE;
fehler:BOOLEAN;
BEGIN
IF (pa<>0) AND (pa<>1) AND (pa<>BACKGNDPAGE)
THEN BEGIN
Error:=Err_InvalidPageNumber; exit
END;
{$I-}
Assign(f,name); fehler:=IOResult<>0;
Rewrite(f,1); fehler:=fehler OR (IOResult<>0);
BlockWrite(f,PICHeader[1],Length(PICHeader));
fehler:=fehler OR (IOResult<>0);
{$I+}
IF fehler
THEN BEGIN
{$I-} Close(f); {$I+}
Error:=Err_FileIO; exit
END;
port[$3ce]:=5; {alten Lese-/Schreibmodus merken}
oldMode:=port[$3cf];
port[$3cf]:=$40; {Lesemodus 0 setzen}
FOR i:=0 TO 3 DO
BEGIN
portw[$3CE]:=4+(i shl 8); {Lese-Plane anwaehlen}
{$I-}
BlockWrite(f,mem[Segment_Adr[pa]:0],PAGESIZE);
{$I+}
fehler:=fehler OR (IOResult<>0);
END;
{$I-}
Close(f);
{$I+}
fehler:=fehler OR (IOResult<>0);
port[$3ce]:=5; {alten Lese-/Schreibmodus setzen}
port[$3cf]:=oldMode;
IF fehler THEN Error:=Err_FileIO
END;
PROCEDURE LoadPage(name:STRING; pa:BYTE);
{ in: name = Filename fuer das zu ladende Bild}
{ pa = Zielseite, in die das Bild geladen werden soll (0..2) }
{ PAGESIZE = Groesse einer Bitplane }
{ PICHeader= Kennung fuer Bilderdateien}
{out: - }
{rem: Die Bitmap-Grafik in der Datei "name" wurde in die Seite "pa" geladen}
VAR f:FILE;
i,oldMode:BYTE;
fehler:BOOLEAN;
s:STRING[3];
BEGIN
IF (pa<>0) AND (pa<>1) AND (pa<>BACKGNDPAGE)
THEN BEGIN
Error:=Err_InvalidPageNumber; exit
END;
{$I-}
Assign(f,name); fehler:=IOResult<>0;
Reset(f,1); fehler:=fehler OR (IOResult<>0);
s[0]:=PICHeader[0];
BlockRead(f,s[1],Length(PICHeader)); fehler:=fehler OR (IOResult<>0);
{$I+}
IF fehler
THEN BEGIN
{$I-} Close(f); {$I+}
Error:=Err_FileIO; exit
END
ELSE IF (FileSize(f)<>4*PAGESIZE+Length(PICHeader)) OR (s<>PICHeader)
THEN BEGIN
{$I-} Close(f); {$I+}
Error:=Err_NoPicture; exit
END;
port[$3ce]:=5; {alten Lese-/Schreibmodus merken}
oldMode:=port[$3cf];
port[$3cf]:=$40; {Schreibmodus 0 setzen}
FOR i:=0 TO 3 DO
BEGIN
portw[$3c4]:=2+(TranslateTab[i] shl 8); {Schreib-Plane anwaehlen}
{$I-}
BlockRead(f,mem[Segment_Adr[pa]:0],PAGESIZE);
{$I+}
fehler:=fehler OR (IOResult<>0);
END;
{$I-}
Close(f);
{$I+}
fehler:=fehler OR (IOResult<>0);
port[$3ce]:=5; {alten Lese-/Schreibmodus setzen}
port[$3cf]:=oldMode;
IF fehler THEN Error:=Err_FileIO
END;
PROCEDURE WriteBackgroundPage(name:STRING);
{ in: name = Filename fuer das abzuspeichernde Hintergrundbild}
{ BACKGNDPAGE= abzuspeichernde Seite (=2) }
{ PAGESIZE = Groesse einer Bitplane }
{ PICHeader = einzutragende Kennung fuer Bilderdateien}
{out: - }
{rem: Die Grafik der Hintergrundseite wurde in der Datei "name" abgelegt}
{ Diese Datei ist 4*PAGESIZE+3 = 64003 Bytes gross: 320x200 Punkte }
{ zu je 1 Byte plus Length(PICHeader) als Kennung}
{ Die Routine ist nur fuer den Hintergrundmodus STATIC sinnvoll }
BEGIN
WritePage(name,BACKGNDPAGE)
END;
PROCEDURE LoadBackgroundPage(name:STRING);
{ in: name = Filename fuer das zu ladende Hintergrundbild}
{ BACKGNDPAGE= Zielseite, in die das Bild geladen werden soll (=2) }
{ PAGESIZE = Groesse einer Bitplane }
{ PICHeader= Kennung fuer Bilderdateien}
{out: - }
{rem: Die Bitmap-Grafik in der Datei "name" wurde in die Hintergrundseite}
{ Die Routine ist nur fuer den Hintergrundmodus STATIC sinnvoll}
{ BACKGNDPAGE geladen}
BEGIN
LoadPage(name,BACKGNDPAGE)
END;
PROCEDURE InitRoutines;
{ in: - }
{out: SpriteN[],SPRITEAD[] wurden auf "gaenzlich leer" initialisiert }
{ NextSprite[] wurde so gesetzt, dass jedes Sprite sein eigener Nach-}
{ folger ist}
{ PAGE, PAGEADR wurden auf die Grafikseite 0 eingestellt}
{ BACKGNDADR wurde auf die Hintergrundseite gesetzt}
{ SCROLLADR wurde auf die scrollbare Hintergrundseite gesetzt}
{ BACKGROUNDMODE wurde auf STATIC gesetzt}
{ StartVirtualX,StartVirtualY = 0 (d.h.: virtuelle = absolute Koord. }
{ oldMode = alter Grafikmodus}
{ Error wurde gesetzt, falls keine VGA-Karte im System enthalten ist }
{ CycleTime = 0, d.h.: keine Mindestzeit fuer einen Animationszyklus }
{ Color = 15, d.h.: weiss (dto.) }
{rem: Diese Prozedur sollte einmal - ganz zu Beginn - zur Initialisierung}
{ des Animationspaketes aufgerufen werden }
VAR i:WORD;
FUNCTION IsVGA:BOOLEAN;
BEGIN
WITH Regs DO
BEGIN
AX:=$1A00;
Intr($10,Regs);
IsVGA:=(AL=$1A) AND {VGA Identify-Adapter-Function unterstuetzt?}
( (BL=7) OR (BL=8) ) {VGAMono oder VGAColor - Adapter}
END;
END;
BEGIN
IF IsVGA THEN Error:=Err_None
ELSE Error:=Err_NoVGA;
SetCycleTime(0);
fillchar(SpriteN,sizeof(SpriteN),0);
fillchar(SPRITEAD,sizeof(SPRITEAD),0);
FOR i:=0 TO LoadMAX DO NextSprite[i]:=i;
BACKGNDADR:=Segment_Adr[BACKGNDPAGE]; {Segmentadresse der Hintergrundseite}
PAGE:=0; {Seite, auf der gezeichnet werden soll}
PAGEADR:=Segment_Adr[PAGE];
SCROLLADR:=Segment_Adr[SCROLLPAGE];
SetBackgroundMode(STATIC);
StartVirtualX:=0; StartVirtualY:=0; {virtuelle = absolute Koordinaten}
Color:=white; {aktuelle Zeichenfarbe sei Weiss }
regs.ah:=$f; intr($10,regs); oldMode:=regs.al;
END;
PROCEDURE CloseRoutines;
{ in: oldMode = alter Grafikmodus, auf den zurueckgeschaltet werden soll}
{out: - }
BEGIN
regs.al:=oldMode; regs.ah:=0; intr($10,regs);
END;
FUNCTION GetErrorMessage:STRING;
{ in: Error = Nummer des aufgetretenen Fehlers}
{out: den Fehler in Worten}
BEGIN
CASE Error OF
Err_None:GetErrorMessage:='No Error';
Err_NotEnoughMemory:GetErrorMessage:='Not enough memory available on heap';
Err_FileIO:GetErrorMessage:='I/O-error with file';
Err_InvalidSpriteNumber:GetErrorMessage:='Invalid sprite number used';
Err_NoSprite:GetErrorMessage:='No (or corrupted) sprite file';
Err_InvalidPageNumber:GetErrorMessage:='Invalid page number used';
Err_NoVGA:GetErrorMessage:='No VGA-card found';
Err_NoPicture:GetErrorMessage:='No (or corrupted) picture file';
Err_InvalidPercentage:GetErrorMessage:='Percentage value must be 0..100';
Err_NoTile:GetErrorMessage:='No (or corrupted) tile/sprite file';
Err_InvalidTileNumber:GetErrorMessage:='Invalid tile number used';
Err_InvalidCoordinates:GetErrorMessage:='Invalid coordinates used';
Err_BackgroundToBig:GetErrorMessage:='Background too big for tile-buffer';
Err_InvalidMode:GetErrorMessage:='Only STATIC or SCROLLING allowed here';
Err_InvalidSpriteLoadNumber:GetErrorMessage:='Invalid spriteload number used';
ELSE GetErrorMessage:='Unknown error';
END;
END;
BEGIN
InitRoutines;
END.